diff --git a/.travis.yml b/.travis.yml index 35c406117fb..ef531a29f2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java index 63cdab890cc..e6d658800d6 100755 --- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java @@ -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. *

- * 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. *

* * @param adapterConfig */ - private void configureProxyForAuthServerIfProvided(AdapterConfig adapterConfig) { + private void configureProxyForAuthServerIfProvided(AdapterHttpClientConfig adapterConfig) { if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) { return; diff --git a/adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java b/adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java index 32bcf39aa54..e73f88c1da0 100755 --- a/adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java +++ b/adapters/oidc/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java @@ -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); diff --git a/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java b/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java index e65d9226f06..2398c95c83b 100755 --- a/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java +++ b/adapters/oidc/undertow/src/main/java/org/keycloak/adapters/undertow/AbstractUndertowKeycloakAuthMech.java @@ -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); diff --git a/adapters/saml/core/nbproject/project.properties b/adapters/saml/core/nbproject/project.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/adapters/saml/core/pom.xml b/adapters/saml/core/pom.xml index 16dce33f2c0..b01061b3965 100755 --- a/adapters/saml/core/pom.xml +++ b/adapters/saml/core/pom.xml @@ -34,6 +34,7 @@ ${maven.build.timestamp} yyyy-MM-dd HH:mm + org.keycloak @@ -70,6 +71,11 @@ junit test + + org.apache.httpcomponents + httpclient + provided + diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/AdapterHttpClientConfig.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/AdapterHttpClientConfig.java new file mode 100644 index 00000000000..5c94fdb964f --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/AdapterHttpClientConfig.java @@ -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. + *

+ * NOTE 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(); + +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpAdapterUtils.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpAdapterUtils.java new file mode 100644 index 00000000000..0fa330e5f6e --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpAdapterUtils.java @@ -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 Hynek Mlnařík + */ +public class HttpAdapterUtils { + + public static MultivaluedHashMap 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 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 extractKeysFromSamlDescriptor(InputStream xmlStream) throws ParsingException { + Object res = new SamlDescriptorIDPKeysExtractor().parse(xmlStream); + return (MultivaluedHashMap) res; + } + +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientAdapterException.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientAdapterException.java new file mode 100644 index 00000000000..e0371adc059 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientAdapterException.java @@ -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 Marek Posolda + */ +public class HttpClientAdapterException extends Exception { + + public HttpClientAdapterException(String message) { + super(message); + } + + public HttpClientAdapterException(String message, Throwable t) { + super(message, t); + } +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientBuilder.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientBuilder.java new file mode 100644 index 00000000000..7e26c01488f --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientBuilder.java @@ -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 Bill Burke + * @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 Bill Burke + * @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. NOTE 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 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. + *

+ * 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. + *

+ * + * @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()); + } +} \ No newline at end of file diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/SniSSLSocketFactory.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/SniSSLSocketFactory.java new file mode 100644 index 00000000000..8c064b0ded2 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/SniSSLSocketFactory.java @@ -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. + * + *

+ * Originally copied from keycloak-adapter-core project. + * + * @author Marko Strukelj + * @author Hynek Mlnařík + */ +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() { + @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; + } +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java index 305ffeb9bc5..6ddf52c6937 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/AbstractInitiateLogin.java @@ -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 + // .addExtension(new KeycloakKeySamlExtensionGenerator()); binding.signDocument(); } return binding; diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java index ee753ade112..a52cdc2ed2d 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/DefaultSamlDeployment.java @@ -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 Bill Burke @@ -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 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; diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java index 0b82ff24cd9..444217785ba 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java @@ -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 Bill Burke * @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(); diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java index 3960b464b3a..de95d877a70 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/IDP.java @@ -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 Bill Burke @@ -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 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; + } + } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java index 0085a6a27f7..1a3dd048509 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java @@ -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"; + } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java index d6e4bce41a4..7af71bac1e5 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java @@ -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 Bill Burke @@ -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()); diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java index e649d1c2598..be54223843f 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java @@ -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 Bill Burke @@ -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 keys = (List)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; diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java index 3eeb1f74c70..be6d6827051 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java @@ -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) diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/descriptor/parsers/SamlDescriptorIDPKeysExtractor.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/descriptor/parsers/SamlDescriptorIDPKeysExtractor.java new file mode 100644 index 00000000000..0858675c503 --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/descriptor/parsers/SamlDescriptorIDPKeysExtractor.java @@ -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 parse(InputStream stream) throws ParsingException { + MultivaluedHashMap 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)); + } + +} diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java index e9247b3bbef..429d610dd4c 100644 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java @@ -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 availableKeys = (Iterable) 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); + } } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/webbrowsersso/WebBrowserSsoAuthenticationHandler.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/webbrowsersso/WebBrowserSsoAuthenticationHandler.java index 5c1454f770a..231c4253376 100755 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/webbrowsersso/WebBrowserSsoAuthenticationHandler.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/webbrowsersso/WebBrowserSsoAuthenticationHandler.java @@ -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 + // .addExtension(new KeycloakKeySamlExtensionGenerator()); } @@ -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 + // .addExtension(new KeycloakKeySamlExtensionGenerator()); } binding.relayState("logout"); diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/rotation/SamlDescriptorPublicKeyLocator.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/rotation/SamlDescriptorPublicKeyLocator.java new file mode 100644 index 00000000000..7a45fb790ce --- /dev/null +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/rotation/SamlDescriptorPublicKeyLocator.java @@ -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 { + + 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 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 signingCerts; + try { + MultivaluedHashMap 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 iterator() { + if (this.publicKeyCache.isEmpty()) { + refreshCertificateCacheAndGet(null); + } + + return this.publicKeyCache.values().iterator(); + } +} diff --git a/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_7.xsd b/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_7.xsd new file mode 100644 index 00000000000..fc9cb5ec7ef --- /dev/null +++ b/adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_7.xsd @@ -0,0 +1,451 @@ + + + + + + + + + + Keycloak SAML Adapter configuration file. + + + + + Describes SAML service provider configuration. + + + + + + + + + + + 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. + + + + + 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. + + + + + 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. + + + + + Describes configuration of SAML identity provider for this service provider. + + + + + + This is the identifier for this client. The IDP needs this value to determine who the client is that is communicating with it. + + + + + SSL policy the adapter will enforce. + + + + + 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. + + + + + URL of the logout page. + + + + + SAML clients can request that a user is re-authenticated even if they are already logged in at the IDP. Default value is false. + + + + + 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. + + + + + 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. + + + + + + + + + Describes a single key used for signing or encryption. + + + + + + + + + Java keystore to load keys and certificates from. + + + + + Private key (PEM format) + + + + + Public key (PEM format) + + + + + Certificate key (PEM format) + + + + + + Flag defining whether the key should be used for signing. + + + + + Flag defining whether the key should be used for encryption + + + + + + + + Private key declaration + + + + + Certificate declaration + + + + + + File path to the key store. + + + + + WAR resource path to the key store. This is a path used in method call to ServletContext.getResourceAsStream(). + + + + + The password of the key store. + + + + + + + Alias that points to the key or cert within the keystore. + + + + + Keystores require an additional password to access private keys. In the PrivateKey element you must define this password within a password attribute. + + + + + + + Alias that points to the key or cert within the keystore. + + + + + + + Policy used to populate value of Java Principal object obtained from methods like HttpServletRequest.getUserPrincipal(). + + + + + Name of the SAML assertion attribute to use within. + + + + + + + + This policy just uses whatever the SAML subject value is. This is the default setting + + + + + 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. + + + + + + + + + All requests must come in via HTTPS. + + + + + Only non-private IP addresses must come over the wire via HTTPS. + + + + + no requests are required to come over via HTTPS. + + + + + + + + + + + + + + + + + + + + + + + Specifies SAML attribute to be converted into roles. + + + + + + + + Specifies name of the SAML attribute to be converted into roles. + + + + + + + + Configuration of the login SAML endpoint of the IDP. + + + + + Configuration of the logout SAML endpoint of the IDP + + + + + 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. + + + + + Configuration of HTTP client used for automatic obtaining of certificates containing public keys for IDP signature verification via SAML descriptor of the IDP. + + + + + + issuer ID of the IDP. + + + + + 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. + + + + + Signature algorithm that the IDP expects signed documents to use. Defaults to RSA_SHA256 + + + + + 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. + + + + + + + + + + + + Should the client sign authn requests? Defaults to whatever the IDP signaturesRequired element value is. + + + + + 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. + + + + + 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. + + + + + SAML binding type used for communicating with the IDP. The default value is POST, but you can set it to REDIRECT as well. + + + + + 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. + + + + + This is the URL for the IDP login service that the client will send requests to. + + + + + + + + Should the client sign authn requests? Defaults to whatever the IDP signaturesRequired element value is. + + + + + Should the client sign logout responses it sends to the IDP requests? Defaults to whatever the IDP signaturesRequired element value is. + + + + + Should the client expect signed logout request documents from the IDP? Defaults to whatever the IDP signaturesRequired element value is. + + + + + Should the client expect signed logout response documents from the IDP? Defaults to whatever the IDP signaturesRequired element value is. + + + + + This is the SAML binding type used for communicating SAML requests to the IDP. The default value is POST. + + + + + This is the SAML binding type used for communicating SAML responses to the IDP. The default value is POST. + + + + + This is the URL for the IDP's logout service when using the POST binding. This setting is REQUIRED if using the POST binding. + + + + + This is the URL for the IDP's logout service when using the REDIRECT binding. This setting is REQUIRED if using the REDIRECT binding. + + + + + + + + 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. + + + + + 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. + + + + + Password for the client keystore and for the client's key. + + + + + Defines number of pooled connections. + + + + + 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. + + + + + URL to HTTP proxy to use for HTTP connections. + + + + + 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. + + + + + + Password for the truststore keystore. + + + + + diff --git a/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java b/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java new file mode 100644 index 00000000000..2c03ef884d5 --- /dev/null +++ b/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java @@ -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 getContent(List objects, Class 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 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) ki.getContent(), hasItem(instanceOf(X509Data.class))); + assertThat((List) 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) ki.getContent(), hasItem(instanceOf(X509Data.class))); + assertThat((List) 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")); + + } + +} diff --git a/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java b/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java new file mode 100755 index 00000000000..b64f181dcc4 --- /dev/null +++ b/adapters/saml/core/src/test/java/org/keycloak/adapters/saml/config/parsers/KeycloakSamlAdapterXMLParserTest.java @@ -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 Bill Burke + * @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)); + } +} diff --git a/adapters/saml/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java b/adapters/saml/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java deleted file mode 100755 index ba01d7548c7..00000000000 --- a/adapters/saml/core/src/test/java/org/keycloak/test/adapters/saml/XmlParserTest.java +++ /dev/null @@ -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 Bill Burke - * @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()); - - - - - } -} diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/cloned/saml-descriptor-valid.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/cloned/saml-descriptor-valid.xml new file mode 100644 index 00000000000..8dd3e41ff78 --- /dev/null +++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/cloned/saml-descriptor-valid.xml @@ -0,0 +1,62 @@ + + + + + + + + rJkJlvowmv1Id74GznieaAC5jU5QQp_ILzuG-GsweTI + + + MIICmzCCAYMCBgFX/9ccIDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYxMDI2MDcxMjUwWhcNMjYxMDI2MDgxNDMwWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPjDrM890OoFWLIU5xNT+v8B8EkpOGY1y/9Yi/yQd95uG/p5LaywiPsw+lPy4tSn1pH/2SxNDST2zynKPDd1lYDev43m0sC2FfD2H73q3udQRqSOxW1e8FrTrGDIHxb82UNrCPlu+fH+xYSkigrkOvLvPigTwSIcu8vgs0lk9FqJ81ty3Wj2e9lS7JJGAJ3pC7rp39VLdJSKbfyj/v2RYBeG5Pscncl8cjUOHUq5u19hThjkU2jOBzgIK2JS0bNmzSfH1eBTZMoCQBI1UJ1IbA8tqjQwpOXc+JkPBRU8T/JUQoQlSR6DTcPFvDgH2oGZYFHFfUontZqtz8jrIt2pxBAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK5VgQp1x1FKgabFI6W/iGuy9ZCRoAixOOEGGkDps6dOEFgTQKTy5D/FZts9KuNxhhiD+NvS0d5BKYa5ITPLVPnGucgYkZhz+/+GhxmbjeQr0eJPaY7ZgLfH3tPA6tfdIkA0iE1En1sKEwt6R6DZjh9jtP9laoUoddTvYaFLJpZ2u1Ik94q6ZqX0fS/RKchaBHjhg6MtqCcHt07CBKHh8XNmKPXVSJC/p0MjyXv+qLaNNqyaAvAw6P6DX1hNjzrdkuaaHGXhu6kkezZUVlDWAm9cd1ppqalSK6ggy7yMW1NWTd/NYOPsFU2TS8DDPzRo14s1Qvw4v+TY6yT0NURJPQA= + + + + + + + BzYc4GwL8HVrAhNyNdp-lTah2DvU9jU03kby9Ynohr4 + + + MIICmzCCAYMCBgFX/9eK7TANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYxMDI2MDcxMzE4WhcNMjYxMDI2MDgxNDU4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCDLT+40/BWzWPSVmpaSaZRs5lBMQ9VP9TCoXkby4PHqxIWRecTPM8fcNkPNPE/tiR2tUIpMXPDzgXNFA/EMoB3V1OEVXPecjKtiZczdR6pi75CBx7PJ2fSXg6xpjhZmHu0k7x591GZdP8Iiu2E6b9QA2p5VXgNgfuP07XzgabnSvIrLG60Imus3u6C2qA/QEuY7EYQWrFooriYLW6B8s3xU8R1a92SLMT8JsfMWXi+1CzAhIbVvdwUwkhVDDhAU6pUek88QQgxodd3FAMksoijCGFN1yrCkovlFhKb3j9AC6Icd9eeJuwYddN/nMeMGEDOeCcAGBACiaUisjUvZDw1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHAHbBI0CRfdw5ZHxHAjgSQvSj41c/4cfwln4Q7X3I5lMBbW3tcgond6Ku9eU46FzG5VpgXIgvEf4u0O9jUnxLlO50+t2SHwQ1RwHdBWQngVSZCRzscq3KrSzx1hx88qLyqcPrr3QtR92fYipDjENxttT/qJtDMrXlwLZEITlHDoneX319USYB9C4zlrCIsQ5XxQTTyCx886Pz15DSVSRxVp61HGk6ROsX/DG5/xwInlzgMZ0r3JWnAjtAaXqUrcwH9FXxco+xkiqKW79bGhWGQI9sXXvQSSNAaENMIUhxtd9uOi1l5e0EkKHE2fHlYyfdUDnFJWwSMXd/NM+hVI4Lw= + + + + + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + + + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + + + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + + + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + + + + + + + \ No newline at end of file diff --git a/adapters/saml/core/src/test/resources/keycloak-saml2.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-invalid.xml similarity index 100% rename from adapters/saml/core/src/test/resources/keycloak-saml2.xml rename to adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-invalid.xml diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-multiple-signing-keys.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-multiple-signing-keys.xml new file mode 100644 index 00000000000..37fc7b5a962 --- /dev/null +++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-multiple-signing-keys.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + private pem + + + public pem + + + + + + + + + + + + + + cert pem 0 + + + cert pem 1 + + + cert pem 2 + + + cert pem 3 + + + + + \ No newline at end of file diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-wth-http-client-settings.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-wth-http-client-settings.xml new file mode 100644 index 00000000000..0c4abb21d75 --- /dev/null +++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml-wth-http-client-settings.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + private pem + + + public pem + + + + + + + + + + + + + + + cert pem + + + + + + + \ No newline at end of file diff --git a/adapters/saml/core/src/test/resources/keycloak-saml.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml.xml similarity index 79% rename from adapters/saml/core/src/test/resources/keycloak-saml.xml rename to adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml.xml index 1ad2a027fcf..193525a894c 100755 --- a/adapters/saml/core/src/test/resources/keycloak-saml.xml +++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/saml/config/parsers/keycloak-saml.xml @@ -15,14 +15,16 @@ ~ limitations under the License. --> - + - + @@ -37,18 +39,18 @@ - + @@ -57,8 +59,8 @@ validateResponseSignature="true" signRequest="true" signResponse="true" - requestBinding="redirect" - responseBinding="post" + requestBinding="REDIRECT" + responseBinding="POST" postBindingUrl="posturl" redirectBindingUrl="redirecturl" /> diff --git a/authz/policy/common/pom.xml b/authz/policy/common/pom.xml index 6149e3a13f2..31df3047981 100644 --- a/authz/policy/common/pom.xml +++ b/authz/policy/common/pom.xml @@ -46,6 +46,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + \ No newline at end of file diff --git a/authz/policy/drools/pom.xml b/authz/policy/drools/pom.xml index 7dc1b4520ec..15943292135 100644 --- a/authz/policy/drools/pom.xml +++ b/authz/policy/drools/pom.xml @@ -23,6 +23,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.keycloak keycloak-services diff --git a/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java b/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java index 3e4839a4345..283eb3eaeea 100755 --- a/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java +++ b/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java @@ -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; diff --git a/common/src/main/java/org/keycloak/common/util/Time.java b/common/src/main/java/org/keycloak/common/util/Time.java index ef5d174a7e4..54809d8dab7 100644 --- a/common/src/main/java/org/keycloak/common/util/Time.java +++ b/common/src/main/java/org/keycloak/common/util/Time.java @@ -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; } diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java index c4818b45c77..0ba327d7331 100755 --- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java +++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java @@ -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; } diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java new file mode 100644 index 00000000000..fa4c87e5c60 --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java @@ -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. + *

+ * NOTE 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(); + +} diff --git a/dependencies/server-min/pom.xml b/dependencies/server-min/pom.xml index 6beae8485ea..1c17f641232 100755 --- a/dependencies/server-min/pom.xml +++ b/dependencies/server-min/pom.xml @@ -59,6 +59,10 @@ org.keycloak keycloak-server-spi + + + org.keycloak + keycloak-server-spi-private org.keycloak diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml index 09d947967a3..4c5af2f8c25 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/drools/main/module.xml @@ -31,6 +31,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml index 8440c1df1b4..849fc353f58 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/eclipse/jdt/core/compiler/ecj/main/module.xml @@ -31,6 +31,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml index 422c683ea55..a8ab0990355 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-common/main/module.xml @@ -29,6 +29,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml index cf36e0a6561..60dc78a87bb 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-authz-policy-drools/main/module.xml @@ -28,6 +28,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml index 4a3d370c8c9..7b024b19e73 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-common/main/module.xml @@ -16,10 +16,6 @@ ~ limitations under the License. --> - - - - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml index 1afc30d5b64..659a055f5c3 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-core/main/module.xml @@ -16,10 +16,6 @@ ~ limitations under the License. --> - - - - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml index d0b9730e2e9..ebb263c7145 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-kerberos-federation/main/module.xml @@ -28,6 +28,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml index 43a1d64c095..dcca1bfd2ee 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-ldap-federation/main/module.xml @@ -28,6 +28,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml index 91f423a4a4a..a80a008a325 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-infinispan/main/module.xml @@ -28,6 +28,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml index 94c80a2a964..88cae4fee59 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-jpa/main/module.xml @@ -29,6 +29,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml index 20cff7cf676..3bd99ae4665 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-model-mongo/main/module.xml @@ -29,6 +29,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi-private/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi-private/main/module.xml new file mode 100755 index 00000000000..0f136fde636 --- /dev/null +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi-private/main/module.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml index 8b5632e30af..daed0af506c 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-server-spi/main/module.xml @@ -16,10 +16,6 @@ ~ limitations under the License. --> - - - - diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml index 4f351fd7c09..9db480c9d07 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml @@ -32,6 +32,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml index ad16f3c4dd1..58939eb49c4 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml @@ -28,5 +28,6 @@ + \ No newline at end of file diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml index 4ee5c508c0a..178470b42c9 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-adduser/main/module.xml @@ -30,6 +30,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml index d785cb7fe37..89aa901572e 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-wildfly-extensions/main/module.xml @@ -28,6 +28,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml index 242b508ba9d..9b41daaf16a 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/kie/main/module.xml @@ -32,11 +32,13 @@ + + diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-api-public/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-api-public/main/module.xml index 451497fd44d..1438ed98691 100755 --- a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-api-public/main/module.xml +++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-api-public/main/module.xml @@ -30,6 +30,7 @@ + diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml index 49e14c40963..4973aa1a4fb 100755 --- a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml +++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml @@ -34,6 +34,7 @@ + diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml index 1d728f5da23..b85e56f2f09 100755 --- a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml +++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml @@ -41,6 +41,7 @@ + diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml index d720b029c61..9d1a63ea476 100755 --- a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml +++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml @@ -39,5 +39,6 @@ + diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core-public/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core-public/main/module.xml index 550dac84b76..35977c71b78 100755 --- a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core-public/main/module.xml +++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core-public/main/module.xml @@ -32,6 +32,7 @@ + diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml index aac95005946..7851fd5395f 100755 --- a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml +++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml @@ -36,6 +36,7 @@ + diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml index f04205bf012..40186d743f3 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml @@ -29,6 +29,7 @@ + diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-api-public/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-api-public/main/module.xml index 451497fd44d..1438ed98691 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-api-public/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-api-public/main/module.xml @@ -30,6 +30,7 @@ + diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml index 34e68950249..e19e0f026d3 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml @@ -34,6 +34,7 @@ + diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core-public/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core-public/main/module.xml index e5e572c4234..563763513df 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core-public/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core-public/main/module.xml @@ -35,6 +35,7 @@ + diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml index aac95005946..7851fd5395f 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml @@ -36,6 +36,7 @@ + diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml index 397901ce735..d4cefc52e38 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml @@ -40,6 +40,7 @@ + diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml index 30271150812..ee00fcc1ae1 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml @@ -41,6 +41,7 @@ + diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-subsystem/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-subsystem/main/module.xml index cda8970632a..857a8e31b89 100755 --- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-subsystem/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-subsystem/main/module.xml @@ -39,5 +39,6 @@ + diff --git a/examples/providers/authenticator/pom.xml b/examples/providers/authenticator/pom.xml index 4e24bf3ad7a..09929660676 100755 --- a/examples/providers/authenticator/pom.xml +++ b/examples/providers/authenticator/pom.xml @@ -41,6 +41,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.jboss.logging jboss-logging diff --git a/examples/providers/domain-extension/README.md b/examples/providers/domain-extension/README.md index 9ec5e63e9a0..465551b2f56 100644 --- a/examples/providers/domain-extension/README.md +++ b/examples/providers/domain-extension/README.md @@ -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: diff --git a/examples/providers/domain-extension/pom.xml b/examples/providers/domain-extension/pom.xml index 43e5cf79175..d31ccbfba48 100755 --- a/examples/providers/domain-extension/pom.xml +++ b/examples/providers/domain-extension/pom.xml @@ -46,6 +46,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.keycloak keycloak-model-jpa diff --git a/examples/providers/event-listener-sysout/README.md b/examples/providers/event-listener-sysout/README.md index 8e1a0850ad0..5540d9b25c4 100644 --- a/examples/providers/event-listener-sysout/README.md +++ b/examples/providers/event-listener-sysout/README.md @@ -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: diff --git a/examples/providers/event-listener-sysout/pom.xml b/examples/providers/event-listener-sysout/pom.xml index 83f348b63e1..3d61dfd9654 100755 --- a/examples/providers/event-listener-sysout/pom.xml +++ b/examples/providers/event-listener-sysout/pom.xml @@ -40,6 +40,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + diff --git a/examples/providers/event-store-mem/README.md b/examples/providers/event-store-mem/README.md index 682ff42b782..2c55109190f 100644 --- a/examples/providers/event-store-mem/README.md +++ b/examples/providers/event-store-mem/README.md @@ -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: diff --git a/examples/providers/event-store-mem/pom.xml b/examples/providers/event-store-mem/pom.xml index 16db1818740..2e48c2dc288 100755 --- a/examples/providers/event-store-mem/pom.xml +++ b/examples/providers/event-store-mem/pom.xml @@ -40,6 +40,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + diff --git a/examples/providers/federation-provider/README.md b/examples/providers/federation-provider/README.md index c90e791547d..ae02fc85c4f 100755 --- a/examples/providers/federation-provider/README.md +++ b/examples/providers/federation-provider/README.md @@ -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: diff --git a/examples/providers/federation-provider/pom.xml b/examples/providers/federation-provider/pom.xml index bd4e1c8a32d..be02cc70579 100755 --- a/examples/providers/federation-provider/pom.xml +++ b/examples/providers/federation-provider/pom.xml @@ -41,6 +41,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.jboss.logging jboss-logging diff --git a/examples/providers/rest/README.md b/examples/providers/rest/README.md index 5ee9327132d..d78e9bc9584 100644 --- a/examples/providers/rest/README.md +++ b/examples/providers/rest/README.md @@ -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: diff --git a/examples/providers/rest/pom.xml b/examples/providers/rest/pom.xml index 416acfcf244..b94cf435ecb 100755 --- a/examples/providers/rest/pom.xml +++ b/examples/providers/rest/pom.xml @@ -41,6 +41,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.jboss.spec.javax.ws.rs jboss-jaxrs-api_2.0_spec diff --git a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java index 837f2b65559..86b2a30ef31 100644 --- a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java +++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java @@ -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 Bill Burke @@ -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); diff --git a/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml b/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml index 60010802709..b2e1b664bf7 100755 --- a/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml +++ b/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml @@ -15,7 +15,9 @@ ~ limitations under the License. --> - + - + - + - + diff --git a/federation/kerberos/pom.xml b/federation/kerberos/pom.xml index 75d3ab4e224..da453d63e5f 100755 --- a/federation/kerberos/pom.xml +++ b/federation/kerberos/pom.xml @@ -40,6 +40,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.jboss.logging jboss-logging diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java index d656a0dbd63..b5f470935b7 100755 --- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java +++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java @@ -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; diff --git a/federation/ldap/pom.xml b/federation/ldap/pom.xml index 4187ef6b29e..38fa19d3ddb 100755 --- a/federation/ldap/pom.xml +++ b/federation/ldap/pom.xml @@ -40,6 +40,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.keycloak keycloak-kerberos-federation diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java index 880f0f70e2c..67cfb45fe71 100755 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java @@ -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; diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java index 460ab62ced6..d135dfa017b 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/group/GroupLDAPFederationMapper.java @@ -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 diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java index 9d6e2a1282e..b45f348fabe 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/membership/role/RoleLDAPFederationMapper.java @@ -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 roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role) - || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); + return RoleUtils.hasRole(roles, role) + || RoleUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/federation/sssd/pom.xml b/federation/sssd/pom.xml index 8430e7cb17e..29113f7c8e3 100644 --- a/federation/sssd/pom.xml +++ b/federation/sssd/pom.xml @@ -60,6 +60,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.jboss.logging jboss-logging diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java index 2820947720e..44662deaa99 100755 --- a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java @@ -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; diff --git a/model/infinispan/pom.xml b/model/infinispan/pom.xml index 08aef53142a..f10fa607519 100755 --- a/model/infinispan/pom.xml +++ b/model/infinispan/pom.xml @@ -38,6 +38,10 @@ org.keycloak keycloak-server-spi + + + org.keycloak + keycloak-server-spi-private provided diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java index bc494404d49..3094521fd43 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java @@ -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 roles = getGroups(); - return KeycloakModelUtils.isMember(roles, group); + return RoleUtils.isMember(roles, group); } @Override diff --git a/model/infinispan/src/test/java/org/keycloak/models/keys/infinispan/InfinispanKeyStorageProviderTest.java b/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java similarity index 97% rename from model/infinispan/src/test/java/org/keycloak/models/keys/infinispan/InfinispanKeyStorageProviderTest.java rename to model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java index 5a15dc3d0d7..e5dd1c1f560 100644 --- a/model/infinispan/src/test/java/org/keycloak/models/keys/infinispan/InfinispanKeyStorageProviderTest.java +++ b/model/infinispan/src/test/java/org/keycloak/keys/infinispan/InfinispanKeyStorageProviderTest.java @@ -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 Marek Posolda diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml index 13f3ae0a988..0b2ae4dee77 100755 --- a/model/jpa/pom.xml +++ b/model/jpa/pom.xml @@ -48,6 +48,10 @@ org.keycloak keycloak-server-spi + + org.keycloak + keycloak-server-spi-private + org.keycloak keycloak-services diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java index 6ea87c55879..3dc7bcc57c0 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/GroupAdapter.java @@ -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 { @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); + return RoleUtils.hasRole(roles, role); } protected TypedQuery getGroupRoleMappingEntityTypedQuery(RoleModel role) { diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java index 5ee5490282e..a95548bfe1c 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java @@ -31,6 +31,7 @@ import org.keycloak.models.jpa.entities.UserGroupMembershipEntity; import org.keycloak.models.jpa.entities.UserRequiredActionEntity; import org.keycloak.models.jpa.entities.UserRoleMappingEntity; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RoleUtils; import javax.persistence.EntityManager; import javax.persistence.Query; @@ -338,7 +339,7 @@ public class UserAdapter implements UserModel, JpaModel { @Override public boolean isMemberOf(GroupModel group) { Set roles = getGroups(); - return KeycloakModelUtils.isMember(roles, group); + return RoleUtils.isMember(roles, group); } protected TypedQuery getUserGroupMappingQuery(GroupModel group) { @@ -352,8 +353,8 @@ public class UserAdapter implements UserModel, JpaModel { @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role) - || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); + return RoleUtils.hasRole(roles, role) + || RoleUtils.hasRoleFromGroup(getGroups(), role, true); } protected TypedQuery getUserRoleMappingEntityTypedQuery(RoleModel role) { diff --git a/model/mongo/pom.xml b/model/mongo/pom.xml index d31fa69aae7..054d3203831 100755 --- a/model/mongo/pom.xml +++ b/model/mongo/pom.xml @@ -50,6 +50,10 @@ org.keycloak keycloak-server-spi + + + org.keycloak + keycloak-server-spi-private provided diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/GroupAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/GroupAdapter.java index 724dd69a067..d4ad3afbc82 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/GroupAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/GroupAdapter.java @@ -28,6 +28,7 @@ import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; import org.keycloak.models.mongo.keycloak.entities.MongoGroupEntity; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RoleUtils; import java.util.ArrayList; import java.util.Collections; @@ -146,7 +147,7 @@ public class GroupAdapter extends AbstractMongoAdapter impleme @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role); + return RoleUtils.hasRole(roles, role); } @Override diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java index 972f4409b77..9d5ad7c6f29 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java @@ -27,6 +27,7 @@ import org.keycloak.models.UserModel; import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity; import org.keycloak.models.mongo.utils.MongoModelUtils; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RoleUtils; import java.util.ArrayList; import java.util.Collections; @@ -262,14 +263,14 @@ public class UserAdapter extends AbstractMongoAdapter implement public boolean isMemberOf(GroupModel group) { if (user.getGroupIds().contains(group.getId())) return true; Set groups = getGroups(); - return KeycloakModelUtils.isMember(groups, group); + return RoleUtils.isMember(groups, group); } @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role) - || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); + return RoleUtils.hasRole(roles, role) + || RoleUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/pom.xml b/pom.xml index 8a61e5b0b4c..7918c6dadea 100755 --- a/pom.xml +++ b/pom.xml @@ -120,7 +120,7 @@ 2.3.7 1.0.1.Final 1.6.5 - + -Xms512m -Xmx2048m -XX:MetaspaceSize=96m -XX:MaxMetaspaceSize=256m @@ -175,6 +175,7 @@ core dependencies server-spi + server-spi-private saml-core-api saml-core proxy @@ -938,6 +939,11 @@ keycloak-server-spi ${project.version} + + org.keycloak + keycloak-server-spi-private + ${project.version} + org.keycloak keycloak-model-jpa @@ -1483,7 +1489,7 @@ - + nexus-staging diff --git a/saml-core/nbproject/project.properties b/saml-core/nbproject/project.properties new file mode 100644 index 00000000000..e69de29bb2d diff --git a/saml-core/pom.xml b/saml-core/pom.xml index 848324097b9..8c08b69f12d 100755 --- a/saml-core/pom.xml +++ b/saml-core/pom.xml @@ -53,6 +53,18 @@ org.apache.santuario xmlsec + + junit + junit + 4.12 + test + + + org.hamcrest + hamcrest-core + 1.3 + test + diff --git a/saml-core/src/main/java/org/keycloak/rotation/CompositeKeyLocator.java b/saml-core/src/main/java/org/keycloak/rotation/CompositeKeyLocator.java new file mode 100644 index 00000000000..4b3cb57a7dd --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/rotation/CompositeKeyLocator.java @@ -0,0 +1,159 @@ +/* + * 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.rotation; + +import java.security.Key; +import java.security.KeyManagementException; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +/** + * {@link KeyLocator} that represents a list of multiple {@link KeyLocator}s. Key is searched + * from the first to the last {@link KeyLocator} in the order given by the list. If there are + * multiple {@link KeyLocator}s providing key with the same key ID, the first matching key is + * returned. + * + * @author hmlnarik + */ +public class CompositeKeyLocator implements KeyLocator, Iterable { + + private final List keyLocators = new LinkedList<>(); + + @Override + public Key getKey(String kid) throws KeyManagementException { + for (KeyLocator keyLocator : keyLocators) { + Key k = keyLocator.getKey(kid); + if (k != null) { + return k; + } + } + + return null; + } + + @Override + public void refreshKeyCache() { + for (KeyLocator keyLocator : keyLocators) { + keyLocator.refreshKeyCache(); + } + } + + /** + * Registers a given {@link KeyLocator} as the first {@link KeyLocator}. + */ + public void addFirst(KeyLocator keyLocator) { + this.keyLocators.add(0, keyLocator); + } + + /** + * Registers a given {@link KeyLocator} as the last {@link KeyLocator}. + */ + public void add(KeyLocator keyLocator) { + this.keyLocators.add(keyLocator); + } + + /** + * Clears the list of registered {@link KeyLocator}s + */ + public void clear() { + this.keyLocators.clear(); + } + + @Override + public String toString() { + if (this.keyLocators.size() == 1) { + return this.keyLocators.get(0).toString(); + } + + StringBuilder sb = new StringBuilder("Key locator chain: ["); + for (Iterator it = keyLocators.iterator(); it.hasNext();) { + KeyLocator keyLocator = it.next(); + sb.append(keyLocator.toString()); + if (it.hasNext()) { + sb.append(", "); + } + } + return sb.append("]").toString(); + } + + @Override + public Iterator iterator() { + final Iterator> iterablesIterator = getKeyLocatorIterators().iterator(); + + return new JointKeyIterator(iterablesIterator).iterator(); + } + + @SuppressWarnings("unchecked") + private Iterable> getKeyLocatorIterators() { + List> res = new LinkedList<>(); + for (KeyLocator kl : this.keyLocators) { + if (kl instanceof Iterable) { + res.add(((Iterable) kl)); + } + } + return Collections.unmodifiableCollection(res); + } + + private class JointKeyIterator implements Iterable { + + // based on http://stackoverflow.com/a/34126154/6930869 + private final Iterator> iterablesIterator; + + public JointKeyIterator(Iterator> iterablesIterator) { + this.iterablesIterator = iterablesIterator; + } + + @Override + public Iterator iterator() { + if (! iterablesIterator.hasNext()) { + return Collections.emptyIterator(); + } + + return new Iterator() { + private Iterator currentIterator = nextIterator(); + + @Override + public boolean hasNext() { + return currentIterator.hasNext(); + } + + @Override + public Key next() { + final Key next = currentIterator.next(); + findNext(); + return next; + } + + private Iterator nextIterator() { + return iterablesIterator.next().iterator(); + } + + private Iterator findNext() { + while (! currentIterator.hasNext()) { + if (! iterablesIterator.hasNext()) { + break; + } + currentIterator = nextIterator(); + } + return this; + } + }.findNext(); + } + } +} diff --git a/saml-core/src/main/java/org/keycloak/rotation/HardcodedKeyLocator.java b/saml-core/src/main/java/org/keycloak/rotation/HardcodedKeyLocator.java new file mode 100644 index 00000000000..ae2615a07b4 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/rotation/HardcodedKeyLocator.java @@ -0,0 +1,69 @@ +/* + * 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.rotation; + +import java.security.Key; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * Key locator that always returns a specified key. + * + * @author Hynek Mlnařík + */ +public class HardcodedKeyLocator implements KeyLocator, Iterable { + + private final Collection keys; + + public HardcodedKeyLocator(Key key) { + this.keys = Collections.singleton(key); + } + + public HardcodedKeyLocator(Collection keys) { + if (keys == null) { + throw new NullPointerException("keys"); + } + this.keys = new LinkedList<>(keys); + } + + @Override + public Key getKey(String kid) { + if (this.keys.size() == 1) { + return this.keys.iterator().next(); + } else { + return null; + } + } + + @Override + public void refreshKeyCache() { + // do nothing + } + + @Override + public String toString() { + return "hardcoded keys, count: " + this.keys.size(); + } + + @Override + public Iterator iterator() { + return Collections.unmodifiableCollection(keys).iterator(); + } +} diff --git a/saml-core/src/main/java/org/keycloak/rotation/KeyLocator.java b/saml-core/src/main/java/org/keycloak/rotation/KeyLocator.java new file mode 100644 index 00000000000..7112eca2996 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/rotation/KeyLocator.java @@ -0,0 +1,50 @@ +/* + * 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.rotation; + +import java.security.Key; +import java.security.KeyManagementException; + +/** + * This interface defines a method for obtaining a security key by ID. + *

+ * If the {@code KeyLocator} implementor wants to make all its keys available for iteration, + * it should implement {@link Iterable}<{@code T extends }{@link Key}> interface. + * The base {@code KeyLocator} does not extend this interface to enable {@code KeyLocators} + * that do not support listing their keys. + * + * @author Hynek Mlnařík + */ +public interface KeyLocator { + + /** + * Returns a key with a particular ID. + * @param kid Key ID + * @param configuration Configuration + * @return key, which should be used for verify signature on given "input" + * @throws KeyManagementException + */ + Key getKey(String kid) throws KeyManagementException; + + /** + * If this key locator caches keys in any way, forces this cache cleanup + * and refreshing the keys. + */ + void refreshKeyCache(); + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/BaseSAML2BindingBuilder.java b/saml-core/src/main/java/org/keycloak/saml/BaseSAML2BindingBuilder.java index 6d84c1301b6..f820a5ece42 100755 --- a/saml-core/src/main/java/org/keycloak/saml/BaseSAML2BindingBuilder.java +++ b/saml-core/src/main/java/org/keycloak/saml/BaseSAML2BindingBuilder.java @@ -38,11 +38,14 @@ import javax.crypto.spec.SecretKeySpec; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.namespace.QName; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URI; +import java.security.InvalidKeyException; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; import java.security.Signature; +import java.security.SignatureException; import java.security.cert.X509Certificate; import static org.keycloak.common.util.HtmlUtils.escapeAttribute; @@ -55,6 +58,7 @@ import static org.keycloak.saml.common.util.StringUtil.isNotNull; public class BaseSAML2BindingBuilder { protected static final Logger logger = Logger.getLogger(BaseSAML2BindingBuilder.class); + protected String signingKeyId; protected KeyPair signingKeyPair; protected X509Certificate signingCertificate; protected boolean sign; @@ -82,23 +86,27 @@ public class BaseSAML2BindingBuilder { return (T)this; } - public T signWith(KeyPair keyPair) { + public T signWith(String signingKeyId, KeyPair keyPair) { + this.signingKeyId = signingKeyId; this.signingKeyPair = keyPair; return (T)this; } - public T signWith(PrivateKey privateKey, PublicKey publicKey) { + public T signWith(String signingKeyId, PrivateKey privateKey, PublicKey publicKey) { + this.signingKeyId = signingKeyId; this.signingKeyPair = new KeyPair(publicKey, privateKey); return (T)this; } - public T signWith(KeyPair keyPair, X509Certificate cert) { + public T signWith(String signingKeyId, KeyPair keyPair, X509Certificate cert) { + this.signingKeyId = signingKeyId; this.signingKeyPair = keyPair; this.signingCertificate = cert; return (T)this; } - public T signWith(PrivateKey privateKey, PublicKey publicKey, X509Certificate cert) { + public T signWith(String signingKeyId, PrivateKey privateKey, PublicKey publicKey, X509Certificate cert) { + this.signingKeyId = signingKeyId; this.signingKeyPair = new KeyPair(publicKey, privateKey); this.signingCertificate = cert; return (T)this; @@ -263,7 +271,7 @@ public class BaseSAML2BindingBuilder { samlSignature.setX509Certificate(signingCertificate); } - samlSignature.signSAMLDocument(samlDocument, signingKeyPair, canonicalizationMethodType); + samlSignature.signSAMLDocument(samlDocument, signingKeyId, signingKeyPair, canonicalizationMethodType); } public void signAssertion(Document samlDocument) throws ProcessingException { @@ -333,7 +341,7 @@ public class BaseSAML2BindingBuilder { public String base64Encoded(Document document) throws ConfigurationException, ProcessingException, IOException { String documentAsString = DocumentUtil.getDocumentAsString(document); - logger.debugv("saml docment: {0}", documentAsString); + logger.debugv("saml document: {0}", documentAsString); byte[] responseBytes = documentAsString.getBytes("UTF-8"); return RedirectBindingUtil.deflateBase64URLEncode(responseBytes); @@ -358,7 +366,7 @@ public class BaseSAML2BindingBuilder { signature.initSign(signingKeyPair.getPrivate()); signature.update(rawQuery.getBytes("UTF-8")); sig = signature.sign(); - } catch (Exception e) { + } catch (InvalidKeyException | UnsupportedEncodingException | SignatureException e) { throw new ProcessingException(e); } String encodedSig = RedirectBindingUtil.base64URLEncode(sig); diff --git a/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java index 302237e6294..ec4fc28a84b 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java +++ b/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java @@ -25,15 +25,19 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import org.w3c.dom.Document; import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; /** * @author pedroigor */ -public class SAML2AuthnRequestBuilder { +public class SAML2AuthnRequestBuilder implements SamlProtocolExtensionsAwareBuilder { private final AuthnRequestType authnRequestType; protected String destination; protected String issuer; + protected final List extensions = new LinkedList<>(); public SAML2AuthnRequestBuilder destination(String destination) { this.destination = destination; @@ -45,6 +49,12 @@ public class SAML2AuthnRequestBuilder { return this; } + @Override + public SAML2AuthnRequestBuilder addExtension(NodeGenerator extension) { + this.extensions.add(extension); + return this; + } + public SAML2AuthnRequestBuilder() { try { this.authnRequestType = new AuthnRequestType(IDGenerator.create("ID_"), XMLTimeUtil.getIssueInstant()); @@ -90,6 +100,14 @@ public class SAML2AuthnRequestBuilder { authnRequestType.setDestination(URI.create(this.destination)); + if (! this.extensions.isEmpty()) { + ExtensionsType extensionsType = new ExtensionsType(); + for (NodeGenerator extension : this.extensions) { + extensionsType.addExtension(extension); + } + authnRequestType.setExtensions(extensionsType); + } + return new SAML2Request().convert(authnRequestType); } catch (Exception e) { throw new RuntimeException("Could not convert " + authnRequestType + " to a document.", e); diff --git a/saml-core/src/main/java/org/keycloak/saml/SAML2ErrorResponseBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SAML2ErrorResponseBuilder.java index 99d1c1fca12..6da6799b428 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SAML2ErrorResponseBuilder.java +++ b/saml-core/src/main/java/org/keycloak/saml/SAML2ErrorResponseBuilder.java @@ -17,7 +17,10 @@ package org.keycloak.saml; +import java.util.LinkedList; +import java.util.List; import org.keycloak.dom.saml.v2.assertion.NameIDType; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; import org.keycloak.dom.saml.v2.protocol.StatusResponseType; import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.common.exceptions.ParsingException; @@ -32,11 +35,12 @@ import org.w3c.dom.Document; * @author Bill Burke * @version $Revision: 1 $ */ -public class SAML2ErrorResponseBuilder { +public class SAML2ErrorResponseBuilder implements SamlProtocolExtensionsAwareBuilder { protected String status; protected String destination; protected String issuer; + protected final List extensions = new LinkedList<>(); public SAML2ErrorResponseBuilder status(String status) { this.status = status; @@ -53,6 +57,11 @@ public class SAML2ErrorResponseBuilder { return this; } + @Override + public SAML2ErrorResponseBuilder addExtension(NodeGenerator extension) { + this.extensions.add(extension); + return this; + } public Document buildDocument() throws ProcessingException { @@ -66,6 +75,14 @@ public class SAML2ErrorResponseBuilder { statusResponse.setIssuer(issuer); statusResponse.setDestination(destination); + if (! this.extensions.isEmpty()) { + ExtensionsType extensionsType = new ExtensionsType(); + for (NodeGenerator extension : this.extensions) { + extensionsType.addExtension(extension); + } + statusResponse.setExtensions(extensionsType); + } + SAML2Response saml2Response = new SAML2Response(); return saml2Response.convert(statusResponse); } catch (ConfigurationException e) { diff --git a/saml-core/src/main/java/org/keycloak/saml/SAML2LoginResponseBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SAML2LoginResponseBuilder.java index 2386edb5099..17dafc737f2 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SAML2LoginResponseBuilder.java +++ b/saml-core/src/main/java/org/keycloak/saml/SAML2LoginResponseBuilder.java @@ -39,6 +39,9 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import org.w3c.dom.Document; import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; import static org.keycloak.saml.common.util.StringUtil.isNotNull; @@ -49,7 +52,7 @@ import static org.keycloak.saml.common.util.StringUtil.isNotNull; * * @author bburke@redhat.com */ -public class SAML2LoginResponseBuilder { +public class SAML2LoginResponseBuilder implements SamlProtocolExtensionsAwareBuilder { protected static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); protected String destination; @@ -64,6 +67,7 @@ public class SAML2LoginResponseBuilder { protected String authMethod; protected String requestIssuer; protected String sessionIndex; + protected final List extensions = new LinkedList<>(); public SAML2LoginResponseBuilder sessionIndex(String sessionIndex) { @@ -136,6 +140,12 @@ public class SAML2LoginResponseBuilder { return this; } + @Override + public SAML2LoginResponseBuilder addExtension(NodeGenerator extension) { + this.extensions.add(extension); + return this; + } + public Document buildDocument(ResponseType responseType) throws ConfigurationException, ProcessingException { Document samlResponseDocument = null; @@ -207,6 +217,14 @@ public class SAML2LoginResponseBuilder { assertion.addStatement(authnStatement); } + if (! this.extensions.isEmpty()) { + ExtensionsType extensionsType = new ExtensionsType(); + for (NodeGenerator extension : this.extensions) { + extensionsType.addExtension(extension); + } + responseType.setExtensions(extensionsType); + } + return responseType; } diff --git a/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutRequestBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutRequestBuilder.java index 99b1cf87e76..d0e81ba49b0 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutRequestBuilder.java +++ b/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutRequestBuilder.java @@ -27,18 +27,22 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import org.w3c.dom.Document; import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class SAML2LogoutRequestBuilder { +public class SAML2LogoutRequestBuilder implements SamlProtocolExtensionsAwareBuilder { protected String userPrincipal; protected String userPrincipalFormat; protected String sessionIndex; protected long assertionExpiration; protected String destination; protected String issuer; + protected final List extensions = new LinkedList<>(); public SAML2LogoutRequestBuilder destination(String destination) { this.destination = destination; @@ -50,6 +54,12 @@ public class SAML2LogoutRequestBuilder { return this; } + @Override + public SAML2LogoutRequestBuilder addExtension(NodeGenerator extension) { + this.extensions.add(extension); + return this; + } + /** * Length of time in seconds the assertion is valid for * See SAML core specification 2.5.1.2 NotOnOrAfter @@ -99,6 +109,15 @@ public class SAML2LogoutRequestBuilder { if (assertionExpiration > 0) lort.setNotOnOrAfter(XMLTimeUtil.add(lort.getIssueInstant(), assertionExpiration * 1000)); lort.setDestination(URI.create(destination)); + + if (! this.extensions.isEmpty()) { + ExtensionsType extensionsType = new ExtensionsType(); + for (NodeGenerator extension : this.extensions) { + extensionsType.addExtension(extension); + } + lort.setExtensions(extensionsType); + } + return lort; } } diff --git a/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutResponseBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutResponseBuilder.java index c00a4d4013f..8050e812cfa 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutResponseBuilder.java +++ b/saml-core/src/main/java/org/keycloak/saml/SAML2LogoutResponseBuilder.java @@ -31,16 +31,20 @@ import org.keycloak.saml.processing.core.saml.v2.util.XMLTimeUtil; import org.w3c.dom.Document; import java.net.URI; +import java.util.LinkedList; +import java.util.List; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; /** * @author Bill Burke * @version $Revision: 1 $ */ -public class SAML2LogoutResponseBuilder { +public class SAML2LogoutResponseBuilder implements SamlProtocolExtensionsAwareBuilder { protected String logoutRequestID; protected String destination; protected String issuer; + protected final List extensions = new LinkedList<>(); public SAML2LogoutResponseBuilder logoutRequestID(String logoutRequestID) { this.logoutRequestID = logoutRequestID; @@ -57,6 +61,11 @@ public class SAML2LogoutResponseBuilder { return this; } + @Override + public SAML2LogoutResponseBuilder addExtension(NodeGenerator extension) { + this.extensions.add(extension); + return this; + } public Document buildDocument() throws ProcessingException { Document samlResponse = null; @@ -77,6 +86,14 @@ public class SAML2LogoutResponseBuilder { statusResponse.setIssuer(issuer); statusResponse.setDestination(destination); + if (! this.extensions.isEmpty()) { + ExtensionsType extensionsType = new ExtensionsType(); + for (NodeGenerator extension : this.extensions) { + extensionsType.addExtension(extension); + } + statusResponse.setExtensions(extensionsType); + } + SAML2Response saml2Response = new SAML2Response(); samlResponse = saml2Response.convert(statusResponse); } catch (ConfigurationException e) { diff --git a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java index e6c10af640b..9a28137694a 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java +++ b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java @@ -22,21 +22,14 @@ package org.keycloak.saml; * @version $Revision: 1 $ */ public class SPMetadataDescriptor { - public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, String entityId, String nameIDPolicyFormat, String certificatePem) { + + public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, String entityId, String nameIDPolicyFormat, String signingCerts) { String descriptor = "\n" + " \n"; - if (wantAuthnRequestsSigned) { - descriptor += - " \n" + - " \n" + - " \n" + - " \n" + certificatePem + "\n" + - " \n" + - " \n" + - " \n" + - " \n"; + if (wantAuthnRequestsSigned && signingCerts != null) { + descriptor += signingCerts; } descriptor += " \n" + @@ -44,10 +37,34 @@ public class SPMetadataDescriptor { " \n" + " \n"; - descriptor += + " index=\"1\" isDefault=\"true\" />\n" + " \n" + "\n"; return descriptor; } + + public static String xmlKeyInfo(String indentation, String keyId, String pemEncodedCertificate, String purpose, boolean declareDSigNamespace) { + if (pemEncodedCertificate == null) { + return ""; + } + + StringBuilder target = new StringBuilder() + .append(indentation).append("\n") + .append(indentation).append(" \n" : ">\n"); + + if (keyId != null) { + target.append(indentation).append(" ").append(keyId).append("\n"); + } + + target + .append(indentation).append(" \n") + .append(indentation).append(" ").append(pemEncodedCertificate).append("\n") + .append(indentation).append(" \n") + .append(indentation).append(" \n") + .append(indentation).append("\n") + ; + + return target.toString(); + } + } diff --git a/saml-core/src/main/java/org/keycloak/saml/SamlProtocolExtensionsAwareBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SamlProtocolExtensionsAwareBuilder.java new file mode 100644 index 00000000000..2192df615e5 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/SamlProtocolExtensionsAwareBuilder.java @@ -0,0 +1,49 @@ +/* + * 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.saml; + +import javax.xml.stream.XMLStreamWriter; +import org.keycloak.saml.common.exceptions.ProcessingException; + +/** + * Implementations of this interface are builders that can register <samlp:Extensions> + * content providers. + * + * @author hmlnarik + */ +public interface SamlProtocolExtensionsAwareBuilder { + + public interface NodeGenerator { + /** + * Generate contents of the <samlp:Extensions> tag. When this method is invoked, + * the writer has already emitted the <samlp:Extensions> start tag. + * + * @param writer Writer to use for producing XML output + * @throws ProcessingException If any exception fails + */ + void write(XMLStreamWriter writer) throws ProcessingException; + } + + /** + * Adds a given node subtree as a SAML protocol extension into the SAML protocol message. + * + * @param extension + * @return + */ + T addExtension(NodeGenerator extension); +} diff --git a/saml-core/src/main/java/org/keycloak/saml/common/DefaultPicketLinkLogger.java b/saml-core/src/main/java/org/keycloak/saml/common/DefaultPicketLinkLogger.java index 76a72ae932a..2f58911090b 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/DefaultPicketLinkLogger.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/DefaultPicketLinkLogger.java @@ -450,6 +450,11 @@ public class DefaultPicketLinkLogger implements PicketLinkLogger { return new RuntimeException(ErrorCodes.EXPECTED_TAG + tag + ">. Found <" + foundElementTag + ">"); } + @Override + public RuntimeException parserExpectedNamespace(String ns, String foundElementNs) { + return new RuntimeException(ErrorCodes.EXPECTED_NAMESPACE + ns + ">. Found <" + foundElementNs + ">"); + } + /* *(non-Javadoc) * @@ -2378,4 +2383,10 @@ public class DefaultPicketLinkLogger implements PicketLinkLogger { return new ProcessingException("Wrong audience [" + serviceURL + "]."); } + @Override + public ProcessingException samlExtensionUnknownChild(Class clazz) { + return new ProcessingException("Unknown child type specified for extension: " + + (clazz == null ? "" : clazz.getSimpleName()) + + "."); + } } diff --git a/saml-core/src/main/java/org/keycloak/saml/common/ErrorCodes.java b/saml-core/src/main/java/org/keycloak/saml/common/ErrorCodes.java index 09f4301c616..37a775504f2 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/ErrorCodes.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/ErrorCodes.java @@ -48,6 +48,8 @@ public interface ErrorCodes { String EXPECTED_TAG = "PL00066: Parser : Expected start tag:"; + String EXPECTED_NAMESPACE = "PL00107: Parser : Expected start element namespace:"; + String EXPECTED_TEXT_VALUE = "PL00071: Parser: Expected text value:"; String EXPECTED_END_TAG = "PL00066: Parser : Expected end tag:"; diff --git a/saml-core/src/main/java/org/keycloak/saml/common/PicketLinkLogger.java b/saml-core/src/main/java/org/keycloak/saml/common/PicketLinkLogger.java index 7ac6a091f8d..91f2f547585 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/PicketLinkLogger.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/PicketLinkLogger.java @@ -296,6 +296,14 @@ public interface PicketLinkLogger { */ RuntimeException parserExpectedTag(String tag, String foundElementTag); + /** + * @param ns + * @param foundElementNs + * + * @return + */ + RuntimeException parserExpectedNamespace(String ns, String foundElementNs); + /** * @param elementName * @@ -1219,4 +1227,6 @@ public interface PicketLinkLogger { RuntimeException parserFeatureNotSupported(String feature); ProcessingException samlAssertionWrongAudience(String serviceURL); + + ProcessingException samlExtensionUnknownChild(Class clazz); } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java index 5ac8ce1c662..49c8df84ccd 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/api/saml/v2/sig/SAML2Signature.java @@ -35,8 +35,8 @@ import javax.xml.crypto.dsig.XMLSignatureException; import javax.xml.parsers.ParserConfigurationException; import java.security.GeneralSecurityException; import java.security.KeyPair; -import java.security.PublicKey; import java.security.cert.X509Certificate; +import org.keycloak.rotation.KeyLocator; /** * Class that deals with SAML2 Signature @@ -121,7 +121,7 @@ public class SAML2Signature { * @throws MarshalException * @throws GeneralSecurityException */ - public Document sign(Document doc, String referenceID, KeyPair keyPair, String canonicalizationMethodType) throws ParserConfigurationException, + public Document sign(Document doc, String referenceID, String keyId, KeyPair keyPair, String canonicalizationMethodType) throws ParserConfigurationException, GeneralSecurityException, MarshalException, XMLSignatureException { String referenceURI = "#" + referenceID; @@ -130,6 +130,7 @@ public class SAML2Signature { if (sibling != null) { SignatureUtilTransferObject dto = new SignatureUtilTransferObject(); dto.setDocumentToBeSigned(doc); + dto.setKeyId(keyId); dto.setKeyPair(keyPair); dto.setDigestMethod(digestMethod); dto.setSignatureMethod(signatureMethod); @@ -142,7 +143,7 @@ public class SAML2Signature { return XMLSignatureUtil.sign(dto, canonicalizationMethodType); } - return XMLSignatureUtil.sign(doc, keyPair, digestMethod, signatureMethod, referenceURI, canonicalizationMethodType); + return XMLSignatureUtil.sign(doc, keyId, keyPair, digestMethod, signatureMethod, referenceURI, canonicalizationMethodType); } /** @@ -153,12 +154,12 @@ public class SAML2Signature { * * @throws org.keycloak.saml.common.exceptions.ProcessingException */ - public void signSAMLDocument(Document samlDocument, KeyPair keypair, String canonicalizationMethodType) throws ProcessingException { + public void signSAMLDocument(Document samlDocument, String keyId, KeyPair keypair, String canonicalizationMethodType) throws ProcessingException { // Get the ID from the root String id = samlDocument.getDocumentElement().getAttribute(ID_ATTRIBUTE_NAME); try { - sign(samlDocument, id, keypair, canonicalizationMethodType); - } catch (Exception e) { + sign(samlDocument, id, keyId, keypair, canonicalizationMethodType); + } catch (ParserConfigurationException | GeneralSecurityException | MarshalException | XMLSignatureException e) { throw new ProcessingException(logger.signatureError(e)); } } @@ -167,20 +168,18 @@ public class SAML2Signature { * Validate the SAML2 Document * * @param signedDocument - * @param publicKey + * @param keyLocator * * @return * * @throws ProcessingException */ - public boolean validate(Document signedDocument, PublicKey publicKey) throws ProcessingException { + public boolean validate(Document signedDocument, KeyLocator keyLocator) throws ProcessingException { try { configureIdAttribute(signedDocument); - return XMLSignatureUtil.validate(signedDocument, publicKey); - } catch (MarshalException me) { + return XMLSignatureUtil.validate(signedDocument, keyLocator); + } catch (MarshalException | XMLSignatureException me) { throw new ProcessingException(logger.signatureError(me)); - } catch (XMLSignatureException xse) { - throw new ProcessingException(logger.signatureError(xse)); } } diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/api/util/KeyInfoTools.java b/saml-core/src/main/java/org/keycloak/saml/processing/api/util/KeyInfoTools.java new file mode 100644 index 00000000000..be9bf51ddbd --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/api/util/KeyInfoTools.java @@ -0,0 +1,60 @@ +/* + * 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.saml.processing.api.util; + +import java.security.cert.X509Certificate; +import javax.xml.crypto.dsig.keyinfo.KeyInfo; +import javax.xml.crypto.dsig.keyinfo.KeyName; +import javax.xml.crypto.dsig.keyinfo.X509Data; + +/** + * Tools for {@link KeyInfo} object manipulation. + * @author hmlnarik + */ +public class KeyInfoTools { + + /** + * Returns the first object of the given class from the given Iterable. + * @param + * @param objects + * @param clazz + * @return The object or {@code null} if not found. + */ + public static T getContent(Iterable objects, Class clazz) { + for (Object o : objects) { + if (clazz.isInstance(o)) { + return (T) o; + } + } + return null; + } + + + public static KeyName getKeyName(KeyInfo keyInfo) { + return getContent(keyInfo.getContent(), KeyName.class); + } + + public static X509Data getX509Data(KeyInfo keyInfo) { + return getContent(keyInfo.getContent(), X509Data.class); + } + + public static X509Certificate getX509Certificate(KeyInfo keyInfo) { + X509Data d = getX509Data(keyInfo); + return d == null ? null : getContent(d.getContent(), X509Certificate.class); + } + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResolveParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResolveParser.java index ca0316b0e28..06d6042fded 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResolveParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResolveParser.java @@ -58,6 +58,8 @@ public class SAMLArtifactResolveParser extends SAMLRequestAbstractParser impleme continue; } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { continue; + } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { + continue; } else throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location=" + startElement.getLocation()); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResponseParser.java index 5f98403dcc2..9d3686bb17f 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResponseParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLArtifactResponseParser.java @@ -68,6 +68,9 @@ public class SAMLArtifactResponseParser extends SAMLStatusResponseTypeParser imp } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { Element sig = StaxParserUtil.getDOMElement(xmlEventReader); response.setSignature(sig); + } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { + SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser(); + response.setExtensions(extensionsParser.parse(xmlEventReader)); } else if (JBossSAMLConstants.AUTHN_REQUEST.get().equals(elementName)) { SAMLAuthNRequestParser authnParser = new SAMLAuthNRequestParser(); AuthnRequestType authn = (AuthnRequestType) authnParser.parse(xmlEventReader); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParser.java index 139e4e08036..6102e9eb1ea 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAttributeQueryParser.java @@ -60,6 +60,8 @@ public class SAMLAttributeQueryParser extends SAMLRequestAbstractParser implemen continue; } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { continue; + } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { + continue; } else throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location=" + startElement.getLocation()); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAuthNRequestParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAuthNRequestParser.java index 5a15b2cabfb..f1d3349ca2d 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAuthNRequestParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLAuthNRequestParser.java @@ -76,6 +76,8 @@ public class SAMLAuthNRequestParser extends SAMLRequestAbstractParser implements continue; } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { continue; + } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { + continue; } else throw new RuntimeException(ErrorCodes.UNKNOWN_START_ELEMENT + elementName + "::location=" + startElement.getLocation()); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLExtensionsParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLExtensionsParser.java new file mode 100644 index 00000000000..5f7ebbb9091 --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLExtensionsParser.java @@ -0,0 +1,82 @@ +/* + * 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.saml.processing.core.parsers.saml; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.events.EndElement; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; +import org.keycloak.saml.common.PicketLinkLogger; +import org.keycloak.saml.common.PicketLinkLoggerFactory; +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.common.parsers.ParserNamespaceSupport; +import org.keycloak.saml.common.util.StaxParserUtil; + +/** + * Parses <samlp:Extensions> SAML2 element into series of DOM nodes. + * + * @author hmlnarik + */ +public class SAMLExtensionsParser implements ParserNamespaceSupport { + + private static final String EXTENSIONS = JBossSAMLConstants.EXTENSIONS.get(); + + private static final PicketLinkLogger logger = PicketLinkLoggerFactory.getLogger(); + + @Override + public ExtensionsType parse(XMLEventReader xmlEventReader) throws ParsingException { + // Get the startelement + StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader); + StaxParserUtil.validate(startElement, EXTENSIONS); + + ExtensionsType extensions = new ExtensionsType(); + + while (xmlEventReader.hasNext()) { + XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader); + if (xmlEvent instanceof EndElement) { + EndElement endElement = (EndElement) xmlEvent; + if (StaxParserUtil.matches(endElement, EXTENSIONS)) { + endElement = StaxParserUtil.getNextEndElement(xmlEventReader); + break; + } else + throw logger.parserUnknownEndElement(StaxParserUtil.getEndElementName(endElement)); + } + + startElement = StaxParserUtil.peekNextStartElement(xmlEventReader); + if (startElement == null) + break; + + extensions.addExtension(StaxParserUtil.getDOMElement(xmlEventReader)); + } + + return extensions; + } + + @Override + public boolean supports(QName qname) { + String nsURI = qname.getNamespaceURI(); + String localPart = qname.getLocalPart(); + + return nsURI.equals(JBossSAMLURIConstants.PROTOCOL_NSURI.get()) + && localPart.equals(JBossSAMLConstants.EXTENSIONS.get()); + } + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLResponseParser.java index a2691c24626..92eaf902285 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLResponseParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLResponseParser.java @@ -71,6 +71,9 @@ public class SAMLResponseParser extends SAMLStatusResponseTypeParser implements } else if (JBossSAMLConstants.ASSERTION.get().equals(elementName)) { SAMLAssertionParser assertionParser = new SAMLAssertionParser(); response.addAssertion(new RTChoiceType((AssertionType) assertionParser.parse(xmlEventReader))); + } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { + SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser(); + response.setExtensions(extensionsParser.parse(xmlEventReader)); } else if (JBossSAMLConstants.STATUS.get().equals(elementName)) { response.setStatus(parseStatus(xmlEventReader)); } else if (JBossSAMLConstants.ENCRYPTED_ASSERTION.get().equals(elementName)) { diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java index f604cf54637..22ed3832a8c 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloRequestParser.java @@ -74,6 +74,8 @@ public class SAMLSloRequestParser extends SAMLRequestAbstractParser implements P continue; } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { continue; + } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { + continue; } else throw logger.parserUnknownTag(elementName, startElement.getLocation()); } diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java index 167a3c5da57..c0f473b1ef2 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/parsers/saml/SAMLSloResponseParser.java @@ -60,6 +60,9 @@ public class SAMLSloResponseParser extends SAMLStatusResponseTypeParser implemen } else if (JBossSAMLConstants.SIGNATURE.get().equals(elementName)) { startElement = StaxParserUtil.getNextStartElement(xmlEventReader); StaxParserUtil.bypassElementBlock(xmlEventReader, JBossSAMLConstants.SIGNATURE.get()); + } else if (JBossSAMLConstants.EXTENSIONS.get().equals(elementName)) { + SAMLExtensionsParser extensionsParser = new SAMLExtensionsParser(); + response.setExtensions(extensionsParser.parse(xmlEventReader)); } else if (JBossSAMLConstants.STATUS.get().equals(elementName)) { response.setStatus(parseStatus(xmlEventReader)); } diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java index 67fb78f5a7b..ed941a09560 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/util/AssertionUtil.java @@ -62,6 +62,7 @@ import java.security.PublicKey; import java.util.ArrayList; import java.util.List; import java.util.Set; +import org.keycloak.rotation.HardcodedKeyLocator; /** * Utility to deal with assertions @@ -276,7 +277,7 @@ public class AssertionUtil { Node n = doc.importNode(assertionElement, true); doc.appendChild(n); - return new SAML2Signature().validate(doc, publicKey); + return new SAML2Signature().validate(doc, new HardcodedKeyLocator(publicKey)); } catch (Exception e) { logger.signatureAssertionValidationError(e); } diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java index 4c041d11c1f..068c91a051f 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/BaseWriter.java @@ -43,8 +43,12 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; +import org.keycloak.saml.SamlProtocolExtensionsAwareBuilder; import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI; +import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI; +import org.w3c.dom.Node; /** * Base Class for the Stax writers for SAML @@ -244,6 +248,28 @@ public class BaseWriter { StaxUtil.flush(writer); } + public void write(ExtensionsType extensions) throws ProcessingException { + if (extensions.getAny().isEmpty()) { + return; + } + + StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.EXTENSIONS.get(), PROTOCOL_NSURI.get()); + + for (Object o : extensions.getAny()) { + if (o instanceof Node) { + StaxUtil.writeDOMNode(writer, (Node) o); + } else if (o instanceof SamlProtocolExtensionsAwareBuilder.NodeGenerator) { + SamlProtocolExtensionsAwareBuilder.NodeGenerator ng = (SamlProtocolExtensionsAwareBuilder.NodeGenerator) o; + ng.write(writer); + } else { + throw logger.samlExtensionUnknownChild(o == null ? null : o.getClass()); + } + } + + StaxUtil.writeEndElement(writer); + StaxUtil.flush(writer); + } + private void write(SubjectConfirmationType subjectConfirmationType) throws ProcessingException { StaxUtil.writeStartElement(writer, ASSERTION_PREFIX, JBossSAMLConstants.SUBJECT_CONFIRMATION.get(), ASSERTION_NSURI.get()); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLRequestWriter.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLRequestWriter.java index 9f99780b014..8c115f51744 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLRequestWriter.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLRequestWriter.java @@ -36,6 +36,7 @@ import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamWriter; import java.net.URI; import java.util.List; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.ASSERTION_NSURI; import static org.keycloak.saml.common.constants.JBossSAMLURIConstants.PROTOCOL_NSURI; @@ -122,6 +123,11 @@ public class SAMLRequestWriter extends BaseWriter { StaxUtil.writeDOMElement(writer, sig); } + ExtensionsType extensions = request.getExtensions(); + if (extensions != null && ! extensions.getAny().isEmpty()) { + write(extensions); + } + NameIDPolicyType nameIDPolicy = request.getNameIDPolicy(); if (nameIDPolicy != null) { write(nameIDPolicy); @@ -171,6 +177,11 @@ public class SAMLRequestWriter extends BaseWriter { StaxUtil.writeDOMElement(writer, signature); } + ExtensionsType extensions = logOutRequest.getExtensions(); + if (extensions != null && ! extensions.getAny().isEmpty()) { + write(extensions); + } + NameIDType nameID = logOutRequest.getNameID(); if (nameID != null) { write(nameID, new QName(ASSERTION_NSURI.get(), JBossSAMLConstants.NAMEID.get(), ASSERTION_PREFIX)); @@ -278,6 +289,11 @@ public class SAMLRequestWriter extends BaseWriter { if (sig != null) { StaxUtil.writeDOMElement(writer, sig); } + ExtensionsType extensions = request.getExtensions(); + if (extensions != null && ! extensions.getAny().isEmpty()) { + write(extensions); + } + String artifact = request.getArtifact(); if (StringUtil.isNotNull(artifact)) { StaxUtil.writeStartElement(writer, PROTOCOL_PREFIX, JBossSAMLConstants.ARTIFACT.get(), PROTOCOL_NSURI.get()); @@ -315,6 +331,10 @@ public class SAMLRequestWriter extends BaseWriter { if (sig != null) { StaxUtil.writeDOMElement(writer, sig); } + ExtensionsType extensions = request.getExtensions(); + if (extensions != null && ! extensions.getAny().isEmpty()) { + write(extensions); + } SubjectType subject = request.getSubject(); if (subject != null) { write(subject); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java index 07fae2a5c04..9327a736511 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/saml/v2/writers/SAMLResponseWriter.java @@ -37,6 +37,7 @@ import javax.xml.namespace.QName; import javax.xml.stream.XMLStreamWriter; import java.net.URI; import java.util.List; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; /** * Write a SAML Response to stream @@ -78,6 +79,10 @@ public class SAMLResponseWriter extends BaseWriter { if (sig != null) { StaxUtil.writeDOMElement(writer, sig); } + ExtensionsType extensions = response.getExtensions(); + if (extensions != null && extensions.getAny() != null && ! extensions.getAny().isEmpty()) { + write(extensions); + } StatusType status = response.getStatus(); write(status); @@ -119,6 +124,10 @@ public class SAMLResponseWriter extends BaseWriter { if (sig != null) { StaxUtil.writeDOMElement(writer, sig); } + ExtensionsType extensions = response.getExtensions(); + if (extensions != null && extensions.getAny() != null && ! extensions.getAny().isEmpty()) { + write(extensions); + } StatusType status = response.getStatus(); if (status != null) { @@ -163,6 +172,15 @@ public class SAMLResponseWriter extends BaseWriter { NameIDType issuer = response.getIssuer(); write(issuer, new QName(JBossSAMLURIConstants.ASSERTION_NSURI.get(), JBossSAMLConstants.ISSUER.get(), ASSERTION_PREFIX)); + Element sig = response.getSignature(); + if (sig != null) { + StaxUtil.writeDOMElement(writer, sig); + } + ExtensionsType extensions = response.getExtensions(); + if (extensions != null && extensions.getAny() != null && ! extensions.getAny().isEmpty()) { + write(extensions); + } + StatusType status = response.getStatus(); write(status); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/KeycloakKeySamlExtensionGenerator.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/KeycloakKeySamlExtensionGenerator.java new file mode 100644 index 00000000000..1bb90ea9b4f --- /dev/null +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/KeycloakKeySamlExtensionGenerator.java @@ -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.saml.processing.core.util; + +import java.util.Objects; +import javax.xml.stream.XMLStreamWriter; +import org.keycloak.saml.SamlProtocolExtensionsAwareBuilder; +import org.keycloak.saml.common.exceptions.ProcessingException; +import org.keycloak.saml.common.util.StaxUtil; +import org.w3c.dom.Element; + +/** + * + * @author hmlnarik + */ +public class KeycloakKeySamlExtensionGenerator implements SamlProtocolExtensionsAwareBuilder.NodeGenerator { + + public static final String NS_URI = "urn:keycloak:ext:key:1.0"; + + public static final String NS_PREFIX = "kckey"; + + public static final String KC_KEY_INFO_ELEMENT_NAME = "KeyInfo"; + + public static final String KEY_ID_ATTRIBUTE_NAME = "MessageSigningKeyId"; + + private final String keyId; + + public KeycloakKeySamlExtensionGenerator(String keyId) { + this.keyId = keyId; + } + + @Override + public void write(XMLStreamWriter writer) throws ProcessingException { + StaxUtil.writeStartElement(writer, NS_PREFIX, KC_KEY_INFO_ELEMENT_NAME, NS_URI); + StaxUtil.writeNameSpace(writer, NS_PREFIX, NS_URI); + if (this.keyId != null) { + StaxUtil.writeAttribute(writer, KEY_ID_ATTRIBUTE_NAME, this.keyId); + } + StaxUtil.writeEndElement(writer); + StaxUtil.flush(writer); + } + + /** + * Checks that the given element is indeed a Keycloak extension {@code KeyInfo} element and + * returns a content of {@code MessageSigningKeyId} attribute in the given element. + * @param element Element to obtain the key info from. + * @return {@code null} if the element is unknown or there is {@code MessageSigningKeyId} attribute unset, + * value of the {@code MessageSigningKeyId} attribute otherwise. + */ + public static String getMessageSigningKeyIdFromElement(Element element) { + if (Objects.equals(element.getNamespaceURI(), NS_URI) && + Objects.equals(element.getLocalName(), KC_KEY_INFO_ELEMENT_NAME) && + element.hasAttribute(KEY_ID_ATTRIBUTE_NAME)) { + return element.getAttribute(KEY_ID_ATTRIBUTE_NAME); + } + + return null; + } + +} diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/SignatureUtilTransferObject.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/SignatureUtilTransferObject.java index 19924e9a762..f8181fe2223 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/SignatureUtilTransferObject.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/SignatureUtilTransferObject.java @@ -32,6 +32,9 @@ public class SignatureUtilTransferObject { private X509Certificate x509Certificate; private Document documentToBeSigned; + + private String keyId; + private KeyPair keyPair; private Node nextSibling; @@ -111,4 +114,12 @@ public class SignatureUtilTransferObject { public void setX509Certificate(X509Certificate x509Certificate) { this.x509Certificate = x509Certificate; } + + public String getKeyId() { + return keyId; + } + + public void setKeyId(String keyId) { + this.keyId = keyId; + } } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLEncryptionUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLEncryptionUtil.java index e767d2911a0..245cff957a0 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLEncryptionUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLEncryptionUtil.java @@ -20,12 +20,14 @@ import org.apache.xml.security.encryption.EncryptedData; import org.apache.xml.security.encryption.EncryptedKey; import org.apache.xml.security.encryption.XMLCipher; import org.apache.xml.security.encryption.XMLEncryptionException; + import org.keycloak.saml.common.PicketLinkLogger; import org.keycloak.saml.common.PicketLinkLoggerFactory; import org.keycloak.saml.common.exceptions.ConfigurationException; import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.common.util.DocumentUtil; import org.keycloak.saml.common.util.StringUtil; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -37,6 +39,7 @@ import java.security.Key; import java.security.PrivateKey; import java.security.PublicKey; import java.util.HashMap; +import java.util.Objects; /** * Utility for XML Encryption Note: This utility is currently using Apache XML Security library API. JSR-106 is @@ -69,6 +72,10 @@ public class XMLEncryptionUtil { private static HashMap algorithms = new HashMap(4); + private static final String RSA_ENCRYPTION_SCHEME = Objects.equals(System.getProperty("keycloak.saml.key_trans.rsa_v1.5"), "true") + ? XMLCipher.RSA_v1dot5 + : XMLCipher.RSA_OAEP; + private static class EncryptionAlgorithm { EncryptionAlgorithm(String jceName, String xmlSecName, int size) { @@ -514,7 +521,7 @@ public class XMLEncryptionUtil { } } if (publicKeyAlgo.contains("RSA")) - return XMLCipher.RSA_v1dot5; + return RSA_ENCRYPTION_SCHEME; if (publicKeyAlgo.contains("DES")) return XMLCipher.TRIPLEDES_KeyWrap; throw logger.unsupportedType("unsupported publicKey Algo:" + publicKeyAlgo); diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java index 98635b7a5ab..193af19dffb 100755 --- a/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/core/util/XMLSignatureUtil.java @@ -54,8 +54,6 @@ import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.dom.DOMValidateContext; import javax.xml.crypto.dsig.keyinfo.KeyInfo; import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; -import javax.xml.crypto.dsig.keyinfo.KeyValue; -import javax.xml.crypto.dsig.keyinfo.X509Data; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import javax.xml.crypto.dsig.spec.TransformParameterSpec; import javax.xml.namespace.QName; @@ -69,6 +67,7 @@ import java.io.OutputStream; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyException; +import java.security.KeyManagementException; import java.security.KeyPair; import java.security.NoSuchProviderException; import java.security.PrivateKey; @@ -79,7 +78,16 @@ import java.security.interfaces.DSAPublicKey; import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; +import javax.xml.crypto.AlgorithmMethod; +import javax.xml.crypto.KeySelector; +import javax.xml.crypto.KeySelectorException; +import javax.xml.crypto.KeySelectorResult; +import javax.xml.crypto.XMLCryptoContext; +import javax.xml.crypto.dsig.keyinfo.KeyName; +import org.keycloak.rotation.KeyLocator; +import org.keycloak.saml.processing.api.util.KeyInfoTools; /** * Utility for XML Signature Note: You can change the canonicalization method type by using the system property @@ -105,15 +113,66 @@ public class XMLSignatureUtil { ; - private static String canonicalizationMethodType = CanonicalizationMethod.EXCLUSIVE; - - private static XMLSignatureFactory fac = getXMLSignatureFactory(); + private static final XMLSignatureFactory fac = getXMLSignatureFactory(); /** * By default, we include the keyinfo in the signature */ private static boolean includeKeyInfoInSignature = true; + private static class KeySelectorUtilizingKeyNameHint extends KeySelector { + + private final KeyLocator locator; + + private boolean keyLocated = false; + + private String keyName = null; + + public KeySelectorUtilizingKeyNameHint(KeyLocator locator) { + this.locator = locator; + } + + @Override + public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) throws KeySelectorException { + try { + KeyName keyNameEl = KeyInfoTools.getKeyName(keyInfo); + this.keyName = keyNameEl == null ? null : keyNameEl.getName(); + final Key key = locator.getKey(keyName); + this.keyLocated = key != null; + return new KeySelectorResult() { + @Override public Key getKey() { + return key; + } + }; + } catch (KeyManagementException ex) { + throw new KeySelectorException(ex); + } + + } + + private boolean wasKeyLocated() { + return this.keyLocated; + } + } + + private static class KeySelectorPresetKey extends KeySelector { + + private final Key key; + + public KeySelectorPresetKey(Key key) { + this.key = key; + } + + @Override + public KeySelectorResult select(KeyInfo keyInfo, KeySelector.Purpose purpose, AlgorithmMethod method, XMLCryptoContext context) { + return new KeySelectorResult() { + @Override public Key getKey() { + return key; + } + }; + } + } + private static XMLSignatureFactory getXMLSignatureFactory() { XMLSignatureFactory xsf = null; @@ -157,7 +216,7 @@ public class XMLSignatureUtil { * @throws MarshalException * @throws GeneralSecurityException */ - public static Document sign(Document doc, Node nodeToBeSigned, KeyPair keyPair, String digestMethod, + public static Document sign(Document doc, Node nodeToBeSigned, String keyId, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI, X509Certificate x509Certificate, String canonicalizationMethodType) throws ParserConfigurationException, GeneralSecurityException, MarshalException, XMLSignatureException { @@ -179,7 +238,7 @@ public class XMLSignatureUtil { if (!referenceURI.isEmpty()) { propagateIDAttributeSetup(nodeToBeSigned, newDoc.getDocumentElement()); } - newDoc = sign(newDoc, keyPair, digestMethod, signatureMethod, referenceURI, x509Certificate, canonicalizationMethodType); + newDoc = sign(newDoc, keyId, keyPair, digestMethod, signatureMethod, referenceURI, x509Certificate, canonicalizationMethodType); // if the signed element is a SAMLv2.0 assertion we need to move the signature element to the position // specified in the schema (before the assertion subject element). @@ -220,10 +279,10 @@ public class XMLSignatureUtil { * @throws MarshalException * @throws XMLSignatureException */ - public static void sign(Element elementToSign, Node nextSibling, KeyPair keyPair, String digestMethod, + public static void sign(Element elementToSign, Node nextSibling, String keyId, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI, String canonicalizationMethodType) throws GeneralSecurityException, MarshalException, XMLSignatureException { - sign(elementToSign, nextSibling, keyPair, digestMethod, signatureMethod, referenceURI, null, canonicalizationMethodType); + sign(elementToSign, nextSibling, keyId, keyPair, digestMethod, signatureMethod, referenceURI, null, canonicalizationMethodType); } /** @@ -242,7 +301,7 @@ public class XMLSignatureUtil { * @throws XMLSignatureException * @since 2.5.0 */ - public static void sign(Element elementToSign, Node nextSibling, KeyPair keyPair, String digestMethod, + public static void sign(Element elementToSign, Node nextSibling, String keyId, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI, X509Certificate x509Certificate, String canonicalizationMethodType) throws GeneralSecurityException, MarshalException, XMLSignatureException { PrivateKey signingKey = keyPair.getPrivate(); @@ -250,7 +309,7 @@ public class XMLSignatureUtil { DOMSignContext dsc = new DOMSignContext(signingKey, elementToSign, nextSibling); - signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, x509Certificate, canonicalizationMethodType); + signImpl(dsc, digestMethod, signatureMethod, referenceURI, keyId, publicKey, x509Certificate, canonicalizationMethodType); } /** @@ -284,9 +343,9 @@ public class XMLSignatureUtil { * @throws XMLSignatureException * @throws MarshalException */ - public static Document sign(Document doc, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI, String canonicalizationMethodType) + public static Document sign(Document doc, String keyId, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI, String canonicalizationMethodType) throws GeneralSecurityException, MarshalException, XMLSignatureException { - return sign(doc, keyPair, digestMethod, signatureMethod, referenceURI, null, canonicalizationMethodType); + return sign(doc, keyId, keyPair, digestMethod, signatureMethod, referenceURI, null, canonicalizationMethodType); } /** @@ -304,7 +363,7 @@ public class XMLSignatureUtil { * @throws MarshalException * @since 2.5.0 */ - public static Document sign(Document doc, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI, + public static Document sign(Document doc, String keyId, KeyPair keyPair, String digestMethod, String signatureMethod, String referenceURI, X509Certificate x509Certificate, String canonicalizationMethodType) throws GeneralSecurityException, MarshalException, XMLSignatureException { logger.trace("Document to be signed=" + DocumentUtil.asString(doc)); @@ -313,7 +372,7 @@ public class XMLSignatureUtil { DOMSignContext dsc = new DOMSignContext(signingKey, doc.getDocumentElement()); - signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, x509Certificate, canonicalizationMethodType); + signImpl(dsc, digestMethod, signatureMethod, referenceURI, keyId, publicKey, x509Certificate, canonicalizationMethodType); return doc; } @@ -331,6 +390,7 @@ public class XMLSignatureUtil { public static Document sign(SignatureUtilTransferObject dto, String canonicalizationMethodType) throws GeneralSecurityException, MarshalException, XMLSignatureException { Document doc = dto.getDocumentToBeSigned(); + String keyId = dto.getKeyId(); KeyPair keyPair = dto.getKeyPair(); Node nextSibling = dto.getNextSibling(); String digestMethod = dto.getDigestMethod(); @@ -344,13 +404,14 @@ public class XMLSignatureUtil { DOMSignContext dsc = new DOMSignContext(signingKey, doc.getDocumentElement(), nextSibling); - signImpl(dsc, digestMethod, signatureMethod, referenceURI, publicKey, dto.getX509Certificate(), canonicalizationMethodType); + signImpl(dsc, digestMethod, signatureMethod, referenceURI, keyId, publicKey, dto.getX509Certificate(), canonicalizationMethodType); return doc; } /** - * Validate a signed document with the given public key + * Validate a signed document with the given public key. All elements that contain a Signature are checked, + * this way both assertions and the containing document are verified when signed. * * @param signedDoc * @param publicKey @@ -361,7 +422,7 @@ public class XMLSignatureUtil { * @throws XMLSignatureException */ @SuppressWarnings("unchecked") - public static boolean validate(Document signedDoc, Key publicKey) throws MarshalException, XMLSignatureException { + public static boolean validate(Document signedDoc, final KeyLocator locator) throws MarshalException, XMLSignatureException { if (signedDoc == null) throw logger.nullArgumentError("Signed Document"); @@ -374,7 +435,7 @@ public class XMLSignatureUtil { return false; } - if (publicKey == null) + if (locator == null) throw logger.nullValueError("Public Key"); int signedAssertions = 0; @@ -390,24 +451,7 @@ public class XMLSignatureUtil { } } - DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(i)); - XMLSignature signature = fac.unmarshalXMLSignature(valContext); - - boolean coreValidity = signature.validate(valContext); - - if (!coreValidity) { - if (logger.isTraceEnabled()) { - boolean sv = signature.getSignatureValue().validate(valContext); - logger.trace("Signature validation status: " + sv); - - List references = signature.getSignedInfo().getReferences(); - for (Reference ref : references) { - logger.trace("[Ref id=" + ref.getId() + ":uri=" + ref.getURI() + "]validity status:" + ref.validate(valContext)); - } - } - - return false; - } + if (! validateSingleNode(signatureNode, locator)) return false; } NodeList assertions = signedDoc.getElementsByTagNameNS(assertionNameSpaceUri, JBossSAMLConstants.ASSERTION.get()); @@ -423,6 +467,62 @@ public class XMLSignatureUtil { return true; } + private static boolean validateSingleNode(Node signatureNode, final KeyLocator locator) throws MarshalException, XMLSignatureException { + KeySelectorUtilizingKeyNameHint sel = new KeySelectorUtilizingKeyNameHint(locator); + try { + if (validateUsingKeySelector(signatureNode, sel)) { + return true; + } + if (sel.wasKeyLocated()) { + return false; + } + } catch (XMLSignatureException ex) { // pass through MarshalException + logger.debug("Verification failed for key " + sel.keyName + ": " + ex); + logger.trace(ex); + } + + logger.trace("Could not validate signature using ds:KeyInfo/ds:KeyName hint."); + + if (locator instanceof Iterable) { + Iterable availableKeys = (Iterable) locator; + + logger.trace("Trying hard to validate XML signature using all available keys."); + + for (Key key : availableKeys) { + try { + if (validateUsingKeySelector(signatureNode, new KeySelectorPresetKey(key))) { + return true; + } + } catch (XMLSignatureException ex) { // pass through MarshalException + logger.debug("Verification failed: " + ex); + logger.trace(ex); + } + } + } + + return false; + } + + private static boolean validateUsingKeySelector(Node signatureNode, KeySelector validationKeySelector) throws XMLSignatureException, MarshalException { + DOMValidateContext valContext = new DOMValidateContext(validationKeySelector, signatureNode); + XMLSignature signature = fac.unmarshalXMLSignature(valContext); + boolean coreValidity = signature.validate(valContext); + + if (! coreValidity) { + if (logger.isTraceEnabled()) { + boolean sv = signature.getSignatureValue().validate(valContext); + logger.trace("Signature validation status: " + sv); + + List references = signature.getSignedInfo().getReferences(); + for (Reference ref : references) { + logger.trace("[Ref id=" + ref.getId() + ":uri=" + ref.getURI() + "]validity status:" + ref.validate(valContext)); + } + } + } + + return coreValidity; + } + /** * Marshall a SignatureType to output stream * @@ -594,7 +694,7 @@ public class XMLSignatureUtil { throw logger.unsupportedType(key.toString()); } - private static void signImpl(DOMSignContext dsc, String digestMethod, String signatureMethod, String referenceURI, PublicKey publicKey, + private static void signImpl(DOMSignContext dsc, String digestMethod, String signatureMethod, String referenceURI, String keyId, PublicKey publicKey, X509Certificate x509Certificate, String canonicalizationMethodType) throws GeneralSecurityException, MarshalException, XMLSignatureException { dsc.setDefaultNamespacePrefix("dsig"); @@ -603,7 +703,7 @@ public class XMLSignatureUtil { Transform transform1 = fac.newTransform(Transform.ENVELOPED, (TransformParameterSpec) null); Transform transform2 = fac.newTransform("http://www.w3.org/2001/10/xml-exc-c14n#", (TransformParameterSpec) null); - List transformList = new ArrayList(); + List transformList = new ArrayList<>(); transformList.add(transform1); transformList.add(transform2); @@ -616,37 +716,34 @@ public class XMLSignatureUtil { SignatureMethod signatureMethodObj = fac.newSignatureMethod(signatureMethod, null); SignedInfo si = fac.newSignedInfo(canonicalizationMethod, signatureMethodObj, referenceList); - KeyInfo ki = null; + KeyInfo ki; if (includeKeyInfoInSignature) { - ki = createKeyInfo(publicKey, x509Certificate); + ki = createKeyInfo(keyId, publicKey, x509Certificate); + } else { + ki = createKeyInfo(keyId, null, null); } XMLSignature signature = fac.newXMLSignature(si, ki); signature.sign(dsc); } - private static KeyInfo createKeyInfo(PublicKey publicKey, X509Certificate x509Certificate) throws KeyException { + private static KeyInfo createKeyInfo(String keyId, PublicKey publicKey, X509Certificate x509Certificate) throws KeyException { KeyInfoFactory keyInfoFactory = fac.getKeyInfoFactory(); - KeyInfo keyInfo = null; - KeyValue keyValue = null; - //Just with public key - if (publicKey != null) { - keyValue = keyInfoFactory.newKeyValue(publicKey); - keyInfo = keyInfoFactory.newKeyInfo(Collections.singletonList(keyValue)); + + List items = new LinkedList<>(); + + if (keyId != null) { + items.add(keyInfoFactory.newKeyName(keyId)); } + if (x509Certificate != null) { - List x509list = new ArrayList(); - - x509list.add(x509Certificate); - X509Data x509Data = keyInfoFactory.newX509Data(x509list); - List items = new ArrayList(); - - items.add(x509Data); - if (keyValue != null) { - items.add(keyValue); - } - keyInfo = keyInfoFactory.newKeyInfo(items); + items.add(keyInfoFactory.newX509Data(Collections.singletonList(x509Certificate))); } - return keyInfo; + + if (publicKey != null) { + items.add(keyInfoFactory.newKeyValue(publicKey)); + } + + return keyInfoFactory.newKeyInfo(items); } } \ No newline at end of file diff --git a/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java new file mode 100644 index 00000000000..ad150e96c5f --- /dev/null +++ b/saml-core/src/test/java/org/keycloak/saml/processing/core/parsers/saml/SAMLParserTest.java @@ -0,0 +1,107 @@ +/* + * 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.saml.processing.core.parsers.saml; + +import java.io.InputStream; +import org.junit.Test; +import static org.junit.Assert.*; +import static org.hamcrest.CoreMatchers.*; +import org.keycloak.dom.saml.v2.protocol.LogoutRequestType; +import org.keycloak.dom.saml.v2.protocol.ResponseType; +import org.w3c.dom.Element; + +/** + * Test class for SAML parser. + * + * TODO: Add further tests. + * + * @author hmlnarik + */ +public class SAMLParserTest { + + @Test + public void testSaml20EncryptedAssertionsSignedReceivedWithRedirectBinding() throws Exception { + InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-encrypted-signed-redirect-response.xml"); + SAMLParser parser = new SAMLParser(); + + Object parsedObject = parser.parse(st); + assertThat(parsedObject, instanceOf(ResponseType.class)); + + ResponseType resp = (ResponseType) parsedObject; + assertThat(resp.getSignature(), nullValue()); + assertThat(resp.getConsent(), nullValue()); + assertThat(resp.getIssuer(), not(nullValue())); + assertThat(resp.getIssuer().getValue(), is("http://localhost:8081/auth/realms/saml-demo")); + + assertThat(resp.getExtensions(), not(nullValue())); + assertThat(resp.getExtensions().getAny().size(), is(1)); + assertThat(resp.getExtensions().getAny().get(0), instanceOf(Element.class)); + Element el = (Element) resp.getExtensions().getAny().get(0); + assertThat(el.getLocalName(), is("KeyInfo")); + assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:key:1.0")); + assertThat(el.hasAttribute("MessageSigningKeyId"), is(true)); + assertThat(el.getAttribute("MessageSigningKeyId"), is("FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE")); + + assertThat(resp.getAssertions(), not(nullValue())); + assertThat(resp.getAssertions().size(), is(1)); + } + + @Test + public void testSaml20EncryptedAssertionsSignedTwoExtensionsReceivedWithRedirectBinding() throws Exception { + Element el; + + InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-encrypted-signed-redirect-response-two-extensions.xml"); + SAMLParser parser = new SAMLParser(); + + Object parsedObject = parser.parse(st); + assertThat(parsedObject, instanceOf(ResponseType.class)); + + ResponseType resp = (ResponseType) parsedObject; + assertThat(resp.getSignature(), nullValue()); + assertThat(resp.getConsent(), nullValue()); + assertThat(resp.getIssuer(), not(nullValue())); + assertThat(resp.getIssuer().getValue(), is("http://localhost:8081/auth/realms/saml-demo")); + + assertThat(resp.getExtensions(), not(nullValue())); + assertThat(resp.getExtensions().getAny().size(), is(2)); + assertThat(resp.getExtensions().getAny().get(0), instanceOf(Element.class)); + el = (Element) resp.getExtensions().getAny().get(0); + assertThat(el.getLocalName(), is("KeyInfo")); + assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:key:1.0")); + assertThat(el.hasAttribute("MessageSigningKeyId"), is(true)); + assertThat(el.getAttribute("MessageSigningKeyId"), is("FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE")); + assertThat(resp.getExtensions().getAny().get(1), instanceOf(Element.class)); + el = (Element) resp.getExtensions().getAny().get(1); + assertThat(el.getLocalName(), is("ever")); + assertThat(el.getNamespaceURI(), is("urn:keycloak:ext:what:1.0")); + assertThat(el.hasAttribute("what"), is(true)); + assertThat(el.getAttribute("what"), is("ever")); + + assertThat(resp.getAssertions(), not(nullValue())); + assertThat(resp.getAssertions().size(), is(1)); + } + + @Test + public void testSaml20PostLogoutRequest() throws Exception { + InputStream st = SAMLParserTest.class.getResourceAsStream("saml20-signed-logout-request.xml"); + SAMLParser parser = new SAMLParser(); + + Object parsedObject = parser.parse(st); + assertThat(parsedObject, instanceOf(LogoutRequestType.class)); + + } +} diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response-two-extensions.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response-two-extensions.xml new file mode 100644 index 00000000000..94a6fdb3750 --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response-two-extensions.xml @@ -0,0 +1,30 @@ + + http://localhost:8081/auth/realms/saml-demo + + + + + + + + + + + + + + + + OkvZTx/ifYLef74rY0F9I8lbJaatgSEguo+zwh5JrYWcO09Ib2gtz5+z+67Is2+wk/OzKp154r8qAI5vY9AYvuXCslKL/wbcZ1UILL78F0T/iiUW3VpWy8Wvz5nezBFPRqot8WiFQykByjlBg1Z8XOts+uIdyqBBi/WjYeJGMaQ= + + + + + + + RW2eu9nP2Ez9hfRlug9xC+kFfVF3HZpEb4kIFH33gmVbzrQjPk0l67uXkwRjC82FZZ482QnHCBIqNFlAryds/zTa6wdRvFmhQnIM6WxoAl8TM+e9h8MoKkalMc8J/Qfp+WQ7/XdmCg2pp9VvUZTK+g0+G4aGuL+S5+ssZq4rl9k7LrSYyp6vj+djgvISZiz5hPYJCN/WY/gWXfVuLHSpu4CmZt8D2APtT3ax1WmGcuzStAfTW8q3MFIDNV59hkpFmDb+gvyLNbZ95cDYxofiPXaC5cOTftnSBp68Ay1eienqdttEDo4fyakszdvq128KwXkH9azCg6sqLxli6B8l2xdq41MeuJO54VqmOhhLxwKy42NtnJvK/NkNwttH4yMwDPpPbC4vOKCXxT2r2F7jjvJQNB2VFv+oiUAWSSc3fGQcc2uNlx9YQVuzTmjqc7fXAWCGgYoogC8AeNWni204bnBoVpFrEo3gzuOe2fFsddJIclglmTH1hWf31FXUHDO2nl/lT4puQVTo+I+d6jpiV+qdp823NDntRxljRlUJO2AzSTXuIIGtF5q5KWyEi9Nj93BCWa1Llcddkn3ZEZMvDwR4MacwUj8G8hwoH73VvT3jAiakjSpNEIqYCzofeejdfN/gEuuAUfe8uNbTu+gBS+iP3QJe3Pc0Fs/lKJzd3frPNj7xb83wpOf865EQQoOozhnRIKKcMReSjakr/Px5NNooeiJcWEreDagQO2TbwTnHg1kCNG3BAXV/2lV3XBU4afZBoUfxAzYWFOl6xFCAPzhQCPL1SFJp1VRADY/1MU2Kaje5AZoJ4jjph8+yspxBvjic1vC1uYRGW8LWRind9w4eVhCm0LfPiFRCpP+jKPQOJzcNH580/nIMFXPHHnLKv/It7Qex1unDv/QjkuCFFHR6SWJm4WBrwDek+MyOIvgT6o878Cu0Ps472QpoYBQ+7l2WoylWdG1lHZV1UiHPj7PLHPNAL4rbbN3U88fS6N9OJHegQTfcX0i/1KPk4IN/5Z+/15dHI658BINjRvI/6O1QqaTVZkqM8ORcoGpn6BjAiz5rRhjWpOCwlmT+VzOAp3IqACURS1X+txjWE2mfVjlHLJsvyGRDLv1dUR3IeStDAEfsjR/ruRgn5XTFpYaccB/u//DJonJr5A+KFiLbYl+sbbSVAoQCAiAdxKdUpKPx7C473UJ2nYQGby5H5xwboa0Uj0SnJLYWdQ0jvVvzWpWFVWATc4UqnaxdoUDAmewrM6cSSIAmQBB34orCunFbriK9Z4efZ7gB9erQ1fpi3z/IjQBoTEpOUUIPW/qMAApIDPVM6UV9PumW7RL9zKEP5PuWJoGGnKbWGP/b9G4vMFiWMaSNHBYYMI6OLH4WJ3E+4QBGh2vjjfQ0gobhaLgIerIwCQFYEdl9KddAjaflUEFXal9fIQ8Bz9L3rDhQE5AGBZL6ULZmJe3GnkN6Cc+UWAGyD5zv2rsCG2lvR5ox4UE2mFi6nBJbC5Vj5m9Sz1l0QpRwUkH2kD2QQ5iV6nNmQOcU/mz7ulxluf8+FBJJimYVqK8UkJ6+W6j8Eft9Q8fTpEuEVLxqTWGgOAEUBf87RWDU+iF3A+AxFGsJLc5RC+5BKNTEDlV2qDCjHT7b5wqBKJ3FHulOih9EenlZiI51m6kg5yyxnMdbhasvSh6Az8Mp/4lFo/wSA/mXxNhBrEEmRhFiIE5yYUEYIj5F8fH+93tIuWQqyhXIwCntEOdSSmoei9EYFzj8deXcEzVf8y/N6HQErZcJjyg34caOsfRcJYoxEiCm4icA/btWhdjUNT02B20qnxGFndO4CRUQlyDqTbyVD8LRLK9/95L9+5v9zojLle8xQe30dsxKn7r9TTJH8QQai5iam9lU1ik50lwTKpZb18k4rNdO5cnnYoHzCXeCg38YZxyFt9G7um/MxlID5Qd5Ywq6thDzL7WxvanKeRhCuJ2MTVV0EoJxZKIj9Yv0Ars9mZHkoHoP0ikcW8d5ciDj1Onnbj+XDcYI3FZj0Y2vToZvYi/7eLWi8EnSjaIQrr/AHnrmZK1w3Uicd691U6r3Y0UdnzQEl4Ub/l1uhSaGAg2oEdDxkOdZ3Frvf/C4nTEBmunPlNvnJjVFssdeVVXKLBOZ5eRiJjasHUKnTeJVwolvd/dBI+ypfw1+5ae/0upxd9/gV1lbwX9N2yOwqbxz24cKXZWvOFBAGc3+gQFu8RrF6NAeQ96PlkuRsiNOKPPtJT3JNrLGvVKY8g== + + + + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response.xml new file mode 100644 index 00000000000..d8d4c151a3c --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-encrypted-signed-redirect-response.xml @@ -0,0 +1,29 @@ + + http://localhost:8081/auth/realms/saml-demo + + + + + + + + + + + + + + + OkvZTx/ifYLef74rY0F9I8lbJaatgSEguo+zwh5JrYWcO09Ib2gtz5+z+67Is2+wk/OzKp154r8qAI5vY9AYvuXCslKL/wbcZ1UILL78F0T/iiUW3VpWy8Wvz5nezBFPRqot8WiFQykByjlBg1Z8XOts+uIdyqBBi/WjYeJGMaQ= + + + + + + + RW2eu9nP2Ez9hfRlug9xC+kFfVF3HZpEb4kIFH33gmVbzrQjPk0l67uXkwRjC82FZZ482QnHCBIqNFlAryds/zTa6wdRvFmhQnIM6WxoAl8TM+e9h8MoKkalMc8J/Qfp+WQ7/XdmCg2pp9VvUZTK+g0+G4aGuL+S5+ssZq4rl9k7LrSYyp6vj+djgvISZiz5hPYJCN/WY/gWXfVuLHSpu4CmZt8D2APtT3ax1WmGcuzStAfTW8q3MFIDNV59hkpFmDb+gvyLNbZ95cDYxofiPXaC5cOTftnSBp68Ay1eienqdttEDo4fyakszdvq128KwXkH9azCg6sqLxli6B8l2xdq41MeuJO54VqmOhhLxwKy42NtnJvK/NkNwttH4yMwDPpPbC4vOKCXxT2r2F7jjvJQNB2VFv+oiUAWSSc3fGQcc2uNlx9YQVuzTmjqc7fXAWCGgYoogC8AeNWni204bnBoVpFrEo3gzuOe2fFsddJIclglmTH1hWf31FXUHDO2nl/lT4puQVTo+I+d6jpiV+qdp823NDntRxljRlUJO2AzSTXuIIGtF5q5KWyEi9Nj93BCWa1Llcddkn3ZEZMvDwR4MacwUj8G8hwoH73VvT3jAiakjSpNEIqYCzofeejdfN/gEuuAUfe8uNbTu+gBS+iP3QJe3Pc0Fs/lKJzd3frPNj7xb83wpOf865EQQoOozhnRIKKcMReSjakr/Px5NNooeiJcWEreDagQO2TbwTnHg1kCNG3BAXV/2lV3XBU4afZBoUfxAzYWFOl6xFCAPzhQCPL1SFJp1VRADY/1MU2Kaje5AZoJ4jjph8+yspxBvjic1vC1uYRGW8LWRind9w4eVhCm0LfPiFRCpP+jKPQOJzcNH580/nIMFXPHHnLKv/It7Qex1unDv/QjkuCFFHR6SWJm4WBrwDek+MyOIvgT6o878Cu0Ps472QpoYBQ+7l2WoylWdG1lHZV1UiHPj7PLHPNAL4rbbN3U88fS6N9OJHegQTfcX0i/1KPk4IN/5Z+/15dHI658BINjRvI/6O1QqaTVZkqM8ORcoGpn6BjAiz5rRhjWpOCwlmT+VzOAp3IqACURS1X+txjWE2mfVjlHLJsvyGRDLv1dUR3IeStDAEfsjR/ruRgn5XTFpYaccB/u//DJonJr5A+KFiLbYl+sbbSVAoQCAiAdxKdUpKPx7C473UJ2nYQGby5H5xwboa0Uj0SnJLYWdQ0jvVvzWpWFVWATc4UqnaxdoUDAmewrM6cSSIAmQBB34orCunFbriK9Z4efZ7gB9erQ1fpi3z/IjQBoTEpOUUIPW/qMAApIDPVM6UV9PumW7RL9zKEP5PuWJoGGnKbWGP/b9G4vMFiWMaSNHBYYMI6OLH4WJ3E+4QBGh2vjjfQ0gobhaLgIerIwCQFYEdl9KddAjaflUEFXal9fIQ8Bz9L3rDhQE5AGBZL6ULZmJe3GnkN6Cc+UWAGyD5zv2rsCG2lvR5ox4UE2mFi6nBJbC5Vj5m9Sz1l0QpRwUkH2kD2QQ5iV6nNmQOcU/mz7ulxluf8+FBJJimYVqK8UkJ6+W6j8Eft9Q8fTpEuEVLxqTWGgOAEUBf87RWDU+iF3A+AxFGsJLc5RC+5BKNTEDlV2qDCjHT7b5wqBKJ3FHulOih9EenlZiI51m6kg5yyxnMdbhasvSh6Az8Mp/4lFo/wSA/mXxNhBrEEmRhFiIE5yYUEYIj5F8fH+93tIuWQqyhXIwCntEOdSSmoei9EYFzj8deXcEzVf8y/N6HQErZcJjyg34caOsfRcJYoxEiCm4icA/btWhdjUNT02B20qnxGFndO4CRUQlyDqTbyVD8LRLK9/95L9+5v9zojLle8xQe30dsxKn7r9TTJH8QQai5iam9lU1ik50lwTKpZb18k4rNdO5cnnYoHzCXeCg38YZxyFt9G7um/MxlID5Qd5Ywq6thDzL7WxvanKeRhCuJ2MTVV0EoJxZKIj9Yv0Ars9mZHkoHoP0ikcW8d5ciDj1Onnbj+XDcYI3FZj0Y2vToZvYi/7eLWi8EnSjaIQrr/AHnrmZK1w3Uicd691U6r3Y0UdnzQEl4Ub/l1uhSaGAg2oEdDxkOdZ3Frvf/C4nTEBmunPlNvnJjVFssdeVVXKLBOZ5eRiJjasHUKnTeJVwolvd/dBI+ypfw1+5ae/0upxd9/gV1lbwX9N2yOwqbxz24cKXZWvOFBAGc3+gQFu8RrF6NAeQ96PlkuRsiNOKPPtJT3JNrLGvVKY8g== + + + + + \ No newline at end of file diff --git a/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-signed-logout-request.xml b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-signed-logout-request.xml new file mode 100644 index 00000000000..8c4ab20704f --- /dev/null +++ b/saml-core/src/test/resources/org/keycloak/saml/processing/core/parsers/saml/saml20-signed-logout-request.xml @@ -0,0 +1,32 @@ + + http://localhost:8080/sales-post-enc/ + + + + + + + + + + + zeWNo5eav5tFOOCEJ1YU9eINkPnBSfixzAr8AOC4R4c= + + + + pyOiS1LsV/XR08zhcN6IqSYuKTDln4otmCvZxCc07ORP1C9jragu8V8rEE09qt/zBcdw7Arb8eLNNC6oCnrnMxuvzRInVTwt7T5K3t0UlzRWOb3HMElhcWFEgDzh6uKw5Cr45A01XNpojtJWCML/qU2Enyyy80FBlCJNcbzyLxE= + + + + + + 2+5MCT5BnVN+IYnKZcH6ev1pjXGi4feE0nOycq/VJ3aeaZMi4G9AxOxCBPupErOC7Kgm/Bw5AdJyw+Q12wSRXfJ9FhqCrLXpb7YOhbVSTJ8De5O8mW35DxAlh/cxe9FXjqPb286wKTUZ3LfGYR+X235UQeCTAPS/Ufi21EXaEik= + + AQAB + + + + + bburke + a3b2df1c-1095-487b-8b56-f62818c449e3 + diff --git a/server-spi-private/pom.xml b/server-spi-private/pom.xml new file mode 100755 index 00000000000..a1d3df23ffd --- /dev/null +++ b/server-spi-private/pom.xml @@ -0,0 +1,98 @@ + + + + + + keycloak-parent + org.keycloak + 2.4.0.CR1-SNAPSHOT + ../pom.xml + + 4.0.0 + + keycloak-server-spi-private + Keycloak Server Private SPI + + + + 1.8 + 1.8 + + + + + org.keycloak + keycloak-server-spi + provided + + + org.jboss.spec.javax.transaction + jboss-transaction-api_1.2_spec + provided + + + org.jboss.resteasy + resteasy-jaxrs + provided + + + org.bouncycastle + bcprov-jdk15on + provided + + + org.bouncycastle + bcpkix-jdk15on + provided + + + org.keycloak + keycloak-core + provided + + + org.jboss.logging + jboss-logging + provided + + + org.apache.httpcomponents + httpclient + provided + + + junit + junit + test + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + diff --git a/server-spi/src/main/java/org/keycloak/ServerStartupError.java b/server-spi-private/src/main/java/org/keycloak/ServerStartupError.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/ServerStartupError.java rename to server-spi-private/src/main/java/org/keycloak/ServerStartupError.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/AbstractAuthenticationFlowContext.java b/server-spi-private/src/main/java/org/keycloak/authentication/AbstractAuthenticationFlowContext.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/AbstractAuthenticationFlowContext.java rename to server-spi-private/src/main/java/org/keycloak/authentication/AbstractAuthenticationFlowContext.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/AuthenticationFlow.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlow.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/AuthenticationFlow.java rename to server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlow.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java rename to server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java rename to server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java rename to server-spi-private/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/Authenticator.java b/server-spi-private/src/main/java/org/keycloak/authentication/Authenticator.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/Authenticator.java rename to server-spi-private/src/main/java/org/keycloak/authentication/Authenticator.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java rename to server-spi-private/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/AuthenticatorSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/AuthenticatorSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/AuthenticatorSpi.java rename to server-spi-private/src/main/java/org/keycloak/authentication/AuthenticatorSpi.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticationFlowContext.java b/server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticationFlowContext.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticationFlowContext.java rename to server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticationFlowContext.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticator.java b/server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticator.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticator.java rename to server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticator.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java b/server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java rename to server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticatorSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/ClientAuthenticatorSpi.java rename to server-spi-private/src/main/java/org/keycloak/authentication/ClientAuthenticatorSpi.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java b/server-spi-private/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java rename to server-spi-private/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/FlowStatus.java b/server-spi-private/src/main/java/org/keycloak/authentication/FlowStatus.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/FlowStatus.java rename to server-spi-private/src/main/java/org/keycloak/authentication/FlowStatus.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/ForkFlowException.java b/server-spi-private/src/main/java/org/keycloak/authentication/ForkFlowException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/ForkFlowException.java rename to server-spi-private/src/main/java/org/keycloak/authentication/ForkFlowException.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/FormAction.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormAction.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/FormAction.java rename to server-spi-private/src/main/java/org/keycloak/authentication/FormAction.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/FormActionFactory.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormActionFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/FormActionFactory.java rename to server-spi-private/src/main/java/org/keycloak/authentication/FormActionFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/FormActionSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormActionSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/FormActionSpi.java rename to server-spi-private/src/main/java/org/keycloak/authentication/FormActionSpi.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/FormAuthenticator.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticator.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/FormAuthenticator.java rename to server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticator.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java rename to server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java rename to server-spi-private/src/main/java/org/keycloak/authentication/FormAuthenticatorSpi.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/FormContext.java b/server-spi-private/src/main/java/org/keycloak/authentication/FormContext.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/FormContext.java rename to server-spi-private/src/main/java/org/keycloak/authentication/FormContext.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/RequiredActionContext.java b/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionContext.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/RequiredActionContext.java rename to server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionContext.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/RequiredActionFactory.java b/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/RequiredActionFactory.java rename to server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/RequiredActionProvider.java b/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/RequiredActionProvider.java rename to server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionProvider.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/RequiredActionSpi.java b/server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/RequiredActionSpi.java rename to server-spi-private/src/main/java/org/keycloak/authentication/RequiredActionSpi.java diff --git a/server-spi/src/main/java/org/keycloak/authentication/ValidationContext.java b/server-spi-private/src/main/java/org/keycloak/authentication/ValidationContext.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authentication/ValidationContext.java rename to server-spi-private/src/main/java/org/keycloak/authentication/ValidationContext.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/AuthorizationProvider.java rename to server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProvider.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/AuthorizationSpi.java b/server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/AuthorizationSpi.java rename to server-spi-private/src/main/java/org/keycloak/authorization/AuthorizationSpi.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/Decision.java b/server-spi-private/src/main/java/org/keycloak/authorization/Decision.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/Decision.java rename to server-spi-private/src/main/java/org/keycloak/authorization/Decision.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java b/server-spi-private/src/main/java/org/keycloak/authorization/attribute/Attributes.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/attribute/Attributes.java rename to server-spi-private/src/main/java/org/keycloak/authorization/attribute/Attributes.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/attribute/package-info.java b/server-spi-private/src/main/java/org/keycloak/authorization/attribute/package-info.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/attribute/package-info.java rename to server-spi-private/src/main/java/org/keycloak/authorization/attribute/package-info.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/identity/Identity.java b/server-spi-private/src/main/java/org/keycloak/authorization/identity/Identity.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/identity/Identity.java rename to server-spi-private/src/main/java/org/keycloak/authorization/identity/Identity.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/identity/package-info.java b/server-spi-private/src/main/java/org/keycloak/authorization/identity/package-info.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/identity/package-info.java rename to server-spi-private/src/main/java/org/keycloak/authorization/identity/package-info.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/Policy.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/Policy.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/model/Policy.java rename to server-spi-private/src/main/java/org/keycloak/authorization/model/Policy.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/Resource.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/model/Resource.java rename to server-spi-private/src/main/java/org/keycloak/authorization/model/Resource.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/ResourceServer.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/ResourceServer.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/model/ResourceServer.java rename to server-spi-private/src/main/java/org/keycloak/authorization/model/ResourceServer.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/Scope.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/Scope.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/model/Scope.java rename to server-spi-private/src/main/java/org/keycloak/authorization/model/Scope.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/model/package-info.java b/server-spi-private/src/main/java/org/keycloak/authorization/model/package-info.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/model/package-info.java rename to server-spi-private/src/main/java/org/keycloak/authorization/model/package-info.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/package-info.java b/server-spi-private/src/main/java/org/keycloak/authorization/package-info.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/package-info.java rename to server-spi-private/src/main/java/org/keycloak/authorization/package-info.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java rename to server-spi-private/src/main/java/org/keycloak/authorization/permission/ResourcePermission.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java rename to server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/Evaluators.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java rename to server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/IterablePermissionEvaluator.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java rename to server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/PermissionEvaluator.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java rename to server-spi-private/src/main/java/org/keycloak/authorization/permission/evaluator/ScheduledPermissionEvaluator.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DecisionResultCollector.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultEvaluation.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/DefaultPolicyEvaluator.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Evaluation.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/EvaluationContext.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/EvaluationContext.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/EvaluationContext.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/EvaluationContext.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/PolicyEvaluator.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PolicyEvaluator.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/PolicyEvaluator.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/PolicyEvaluator.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Result.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Result.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/Result.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/Result.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/package-info.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/package-info.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/evaluation/package-info.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/evaluation/package-info.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProvider.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderAdminService.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicySpi.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicySpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/provider/PolicySpi.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/PolicySpi.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/policy/provider/package-info.java b/server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/package-info.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/policy/provider/package-info.java rename to server-spi-private/src/main/java/org/keycloak/authorization/policy/provider/package-info.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/AuthorizationStoreFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/PolicyStore.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/PolicyStore.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/ResourceServerStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceServerStore.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/ResourceServerStore.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceServerStore.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/ResourceStore.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/ResourceStore.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/ScopeStore.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/ScopeStore.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/StoreFactory.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/StoreFactory.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactory.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/StoreFactorySpi.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/package-info.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/package-info.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/package-info.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/package-info.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/ClientApplicationSynchronizer.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/RealmSynchronizer.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/Synchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/Synchronizer.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/syncronization/Synchronizer.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/Synchronizer.java diff --git a/server-spi/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java b/server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java rename to server-spi-private/src/main/java/org/keycloak/authorization/store/syncronization/UserSynchronizer.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProvider.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/AuthenticationRequest.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/AuthenticationRequest.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/AuthenticationRequest.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/AuthenticationRequest.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/ConfigConstants.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/ConfigConstants.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/ConfigConstants.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/ConfigConstants.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/DefaultDataMarshaller.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/DefaultDataMarshaller.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/DefaultDataMarshaller.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/DefaultDataMarshaller.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityBrokerException.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProvider.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/IdentityProvider.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProvider.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderDataMarshaller.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderDataMarshaller.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderDataMarshaller.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderDataMarshaller.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderMapperSpi.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/IdentityProviderSpi.java diff --git a/server-spi/src/main/java/org/keycloak/broker/provider/util/SimpleHttp.java b/server-spi-private/src/main/java/org/keycloak/broker/provider/util/SimpleHttp.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/provider/util/SimpleHttp.java rename to server-spi-private/src/main/java/org/keycloak/broker/provider/util/SimpleHttp.java diff --git a/server-spi/src/main/java/org/keycloak/broker/social/SocialIdentityProvider.java b/server-spi-private/src/main/java/org/keycloak/broker/social/SocialIdentityProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/social/SocialIdentityProvider.java rename to server-spi-private/src/main/java/org/keycloak/broker/social/SocialIdentityProvider.java diff --git a/server-spi/src/main/java/org/keycloak/broker/social/SocialIdentityProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/broker/social/SocialIdentityProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/social/SocialIdentityProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/broker/social/SocialIdentityProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/broker/social/SocialProviderSpi.java b/server-spi-private/src/main/java/org/keycloak/broker/social/SocialProviderSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/broker/social/SocialProviderSpi.java rename to server-spi-private/src/main/java/org/keycloak/broker/social/SocialProviderSpi.java diff --git a/server-spi/src/main/java/org/keycloak/cluster/ClusterEvent.java b/server-spi-private/src/main/java/org/keycloak/cluster/ClusterEvent.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/cluster/ClusterEvent.java rename to server-spi-private/src/main/java/org/keycloak/cluster/ClusterEvent.java diff --git a/server-spi/src/main/java/org/keycloak/cluster/ClusterListener.java b/server-spi-private/src/main/java/org/keycloak/cluster/ClusterListener.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/cluster/ClusterListener.java rename to server-spi-private/src/main/java/org/keycloak/cluster/ClusterListener.java diff --git a/server-spi/src/main/java/org/keycloak/cluster/ClusterProvider.java b/server-spi-private/src/main/java/org/keycloak/cluster/ClusterProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/cluster/ClusterProvider.java rename to server-spi-private/src/main/java/org/keycloak/cluster/ClusterProvider.java diff --git a/server-spi/src/main/java/org/keycloak/cluster/ClusterProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/cluster/ClusterProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/cluster/ClusterProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/cluster/ClusterProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/cluster/ClusterSpi.java b/server-spi-private/src/main/java/org/keycloak/cluster/ClusterSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/cluster/ClusterSpi.java rename to server-spi-private/src/main/java/org/keycloak/cluster/ClusterSpi.java diff --git a/server-spi/src/main/java/org/keycloak/cluster/ExecutionResult.java b/server-spi-private/src/main/java/org/keycloak/cluster/ExecutionResult.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/cluster/ExecutionResult.java rename to server-spi-private/src/main/java/org/keycloak/cluster/ExecutionResult.java diff --git a/server-spi/src/main/java/org/keycloak/connections/httpclient/HttpClientFactory.java b/server-spi-private/src/main/java/org/keycloak/connections/httpclient/HttpClientFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/connections/httpclient/HttpClientFactory.java rename to server-spi-private/src/main/java/org/keycloak/connections/httpclient/HttpClientFactory.java diff --git a/server-spi/src/main/java/org/keycloak/connections/httpclient/HttpClientProvider.java b/server-spi-private/src/main/java/org/keycloak/connections/httpclient/HttpClientProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/connections/httpclient/HttpClientProvider.java rename to server-spi-private/src/main/java/org/keycloak/connections/httpclient/HttpClientProvider.java diff --git a/server-spi/src/main/java/org/keycloak/connections/httpclient/HttpClientSpi.java b/server-spi-private/src/main/java/org/keycloak/connections/httpclient/HttpClientSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/connections/httpclient/HttpClientSpi.java rename to server-spi-private/src/main/java/org/keycloak/connections/httpclient/HttpClientSpi.java diff --git a/server-spi/src/main/java/org/keycloak/email/EmailException.java b/server-spi-private/src/main/java/org/keycloak/email/EmailException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/email/EmailException.java rename to server-spi-private/src/main/java/org/keycloak/email/EmailException.java diff --git a/server-spi/src/main/java/org/keycloak/email/EmailSenderProvider.java b/server-spi-private/src/main/java/org/keycloak/email/EmailSenderProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/email/EmailSenderProvider.java rename to server-spi-private/src/main/java/org/keycloak/email/EmailSenderProvider.java diff --git a/server-spi/src/main/java/org/keycloak/email/EmailSenderProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/email/EmailSenderProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/email/EmailSenderProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/email/EmailSenderProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/email/EmailSenderSpi.java b/server-spi-private/src/main/java/org/keycloak/email/EmailSenderSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/email/EmailSenderSpi.java rename to server-spi-private/src/main/java/org/keycloak/email/EmailSenderSpi.java diff --git a/server-spi/src/main/java/org/keycloak/email/EmailTemplateProvider.java b/server-spi-private/src/main/java/org/keycloak/email/EmailTemplateProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/email/EmailTemplateProvider.java rename to server-spi-private/src/main/java/org/keycloak/email/EmailTemplateProvider.java diff --git a/server-spi/src/main/java/org/keycloak/email/EmailTemplateProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/email/EmailTemplateProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/email/EmailTemplateProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/email/EmailTemplateProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/email/EmailTemplateSpi.java b/server-spi-private/src/main/java/org/keycloak/email/EmailTemplateSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/email/EmailTemplateSpi.java rename to server-spi-private/src/main/java/org/keycloak/email/EmailTemplateSpi.java diff --git a/server-spi/src/main/java/org/keycloak/events/Details.java b/server-spi-private/src/main/java/org/keycloak/events/Details.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/Details.java rename to server-spi-private/src/main/java/org/keycloak/events/Details.java diff --git a/server-spi/src/main/java/org/keycloak/events/Errors.java b/server-spi-private/src/main/java/org/keycloak/events/Errors.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/Errors.java rename to server-spi-private/src/main/java/org/keycloak/events/Errors.java diff --git a/server-spi/src/main/java/org/keycloak/events/Event.java b/server-spi-private/src/main/java/org/keycloak/events/Event.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/Event.java rename to server-spi-private/src/main/java/org/keycloak/events/Event.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventBuilder.java b/server-spi-private/src/main/java/org/keycloak/events/EventBuilder.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventBuilder.java rename to server-spi-private/src/main/java/org/keycloak/events/EventBuilder.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventListenerProvider.java b/server-spi-private/src/main/java/org/keycloak/events/EventListenerProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventListenerProvider.java rename to server-spi-private/src/main/java/org/keycloak/events/EventListenerProvider.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventListenerProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/events/EventListenerProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventListenerProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/events/EventListenerProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventListenerSpi.java b/server-spi-private/src/main/java/org/keycloak/events/EventListenerSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventListenerSpi.java rename to server-spi-private/src/main/java/org/keycloak/events/EventListenerSpi.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventQuery.java b/server-spi-private/src/main/java/org/keycloak/events/EventQuery.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventQuery.java rename to server-spi-private/src/main/java/org/keycloak/events/EventQuery.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventStoreProvider.java b/server-spi-private/src/main/java/org/keycloak/events/EventStoreProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventStoreProvider.java rename to server-spi-private/src/main/java/org/keycloak/events/EventStoreProvider.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventStoreProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/events/EventStoreProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventStoreProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/events/EventStoreProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventStoreSpi.java b/server-spi-private/src/main/java/org/keycloak/events/EventStoreSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventStoreSpi.java rename to server-spi-private/src/main/java/org/keycloak/events/EventStoreSpi.java diff --git a/server-spi/src/main/java/org/keycloak/events/EventType.java b/server-spi-private/src/main/java/org/keycloak/events/EventType.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/EventType.java rename to server-spi-private/src/main/java/org/keycloak/events/EventType.java diff --git a/server-spi/src/main/java/org/keycloak/events/admin/AdminEvent.java b/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/admin/AdminEvent.java rename to server-spi-private/src/main/java/org/keycloak/events/admin/AdminEvent.java diff --git a/server-spi/src/main/java/org/keycloak/events/admin/AdminEventQuery.java b/server-spi-private/src/main/java/org/keycloak/events/admin/AdminEventQuery.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/admin/AdminEventQuery.java rename to server-spi-private/src/main/java/org/keycloak/events/admin/AdminEventQuery.java diff --git a/server-spi/src/main/java/org/keycloak/events/admin/AuthDetails.java b/server-spi-private/src/main/java/org/keycloak/events/admin/AuthDetails.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/admin/AuthDetails.java rename to server-spi-private/src/main/java/org/keycloak/events/admin/AuthDetails.java diff --git a/server-spi/src/main/java/org/keycloak/events/admin/AuthQuery.java b/server-spi-private/src/main/java/org/keycloak/events/admin/AuthQuery.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/admin/AuthQuery.java rename to server-spi-private/src/main/java/org/keycloak/events/admin/AuthQuery.java diff --git a/server-spi/src/main/java/org/keycloak/events/admin/OperationType.java b/server-spi-private/src/main/java/org/keycloak/events/admin/OperationType.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/admin/OperationType.java rename to server-spi-private/src/main/java/org/keycloak/events/admin/OperationType.java diff --git a/server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java b/server-spi-private/src/main/java/org/keycloak/events/admin/ResourceType.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/events/admin/ResourceType.java rename to server-spi-private/src/main/java/org/keycloak/events/admin/ResourceType.java diff --git a/server-spi/src/main/java/org/keycloak/exportimport/ExportProvider.java b/server-spi-private/src/main/java/org/keycloak/exportimport/ExportProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/exportimport/ExportProvider.java rename to server-spi-private/src/main/java/org/keycloak/exportimport/ExportProvider.java diff --git a/server-spi/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/exportimport/ExportProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/exportimport/ExportSpi.java b/server-spi-private/src/main/java/org/keycloak/exportimport/ExportSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/exportimport/ExportSpi.java rename to server-spi-private/src/main/java/org/keycloak/exportimport/ExportSpi.java diff --git a/server-spi/src/main/java/org/keycloak/exportimport/ImportProvider.java b/server-spi-private/src/main/java/org/keycloak/exportimport/ImportProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/exportimport/ImportProvider.java rename to server-spi-private/src/main/java/org/keycloak/exportimport/ImportProvider.java diff --git a/server-spi/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/exportimport/ImportProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/exportimport/ImportSpi.java b/server-spi-private/src/main/java/org/keycloak/exportimport/ImportSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/exportimport/ImportSpi.java rename to server-spi-private/src/main/java/org/keycloak/exportimport/ImportSpi.java diff --git a/server-spi/src/main/java/org/keycloak/exportimport/Strategy.java b/server-spi-private/src/main/java/org/keycloak/exportimport/Strategy.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/exportimport/Strategy.java rename to server-spi-private/src/main/java/org/keycloak/exportimport/Strategy.java diff --git a/server-spi/src/main/java/org/keycloak/exportimport/UsersExportStrategy.java b/server-spi-private/src/main/java/org/keycloak/exportimport/UsersExportStrategy.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/exportimport/UsersExportStrategy.java rename to server-spi-private/src/main/java/org/keycloak/exportimport/UsersExportStrategy.java diff --git a/server-spi/src/main/java/org/keycloak/forms/account/AccountPages.java b/server-spi-private/src/main/java/org/keycloak/forms/account/AccountPages.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/forms/account/AccountPages.java rename to server-spi-private/src/main/java/org/keycloak/forms/account/AccountPages.java diff --git a/server-spi/src/main/java/org/keycloak/forms/account/AccountProvider.java b/server-spi-private/src/main/java/org/keycloak/forms/account/AccountProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/forms/account/AccountProvider.java rename to server-spi-private/src/main/java/org/keycloak/forms/account/AccountProvider.java diff --git a/server-spi/src/main/java/org/keycloak/forms/account/AccountProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/forms/account/AccountProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/forms/account/AccountProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/forms/account/AccountProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/forms/account/AccountSpi.java b/server-spi-private/src/main/java/org/keycloak/forms/account/AccountSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/forms/account/AccountSpi.java rename to server-spi-private/src/main/java/org/keycloak/forms/account/AccountSpi.java diff --git a/server-spi/src/main/java/org/keycloak/forms/login/LoginFormsPages.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/forms/login/LoginFormsPages.java rename to server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsPages.java diff --git a/server-spi/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java rename to server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProvider.java diff --git a/server-spi/src/main/java/org/keycloak/forms/login/LoginFormsProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/forms/login/LoginFormsProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/forms/login/LoginFormsSpi.java b/server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/forms/login/LoginFormsSpi.java rename to server-spi-private/src/main/java/org/keycloak/forms/login/LoginFormsSpi.java diff --git a/server-spi/src/main/java/org/keycloak/keys/KeyProvider.java b/server-spi-private/src/main/java/org/keycloak/keys/KeyProvider.java similarity index 97% rename from server-spi/src/main/java/org/keycloak/keys/KeyProvider.java rename to server-spi-private/src/main/java/org/keycloak/keys/KeyProvider.java index 31ca54194fe..5eebe818ebd 100644 --- a/server-spi/src/main/java/org/keycloak/keys/KeyProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/keys/KeyProvider.java @@ -21,7 +21,6 @@ import org.keycloak.provider.Provider; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.List; diff --git a/server-spi/src/main/java/org/keycloak/keys/KeyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/keys/KeyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/keys/KeyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/keys/KeyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/keys/KeySpi.java b/server-spi-private/src/main/java/org/keycloak/keys/KeySpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/keys/KeySpi.java rename to server-spi-private/src/main/java/org/keycloak/keys/KeySpi.java diff --git a/server-spi/src/main/java/org/keycloak/keys/PublicKeyLoader.java b/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyLoader.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/keys/PublicKeyLoader.java rename to server-spi-private/src/main/java/org/keycloak/keys/PublicKeyLoader.java diff --git a/server-spi/src/main/java/org/keycloak/keys/PublicKeyStorageProvider.java b/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/keys/PublicKeyStorageProvider.java rename to server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageProvider.java diff --git a/server-spi/src/main/java/org/keycloak/keys/PublicKeyStorageProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/keys/PublicKeyStorageProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/keys/PublicKeyStorageSpi.java b/server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/keys/PublicKeyStorageSpi.java rename to server-spi-private/src/main/java/org/keycloak/keys/PublicKeyStorageSpi.java diff --git a/server-spi/src/main/java/org/keycloak/mappers/FederationConfigValidationException.java b/server-spi-private/src/main/java/org/keycloak/mappers/FederationConfigValidationException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/mappers/FederationConfigValidationException.java rename to server-spi-private/src/main/java/org/keycloak/mappers/FederationConfigValidationException.java diff --git a/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapper.java b/server-spi-private/src/main/java/org/keycloak/mappers/UserFederationMapper.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/mappers/UserFederationMapper.java rename to server-spi-private/src/main/java/org/keycloak/mappers/UserFederationMapper.java diff --git a/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java b/server-spi-private/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java rename to server-spi-private/src/main/java/org/keycloak/mappers/UserFederationMapperFactory.java diff --git a/server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperSpi.java b/server-spi-private/src/main/java/org/keycloak/mappers/UserFederationMapperSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/mappers/UserFederationMapperSpi.java rename to server-spi-private/src/main/java/org/keycloak/mappers/UserFederationMapperSpi.java diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/MigrationModelManager.java rename to server-spi-private/src/main/java/org/keycloak/migration/MigrationModelManager.java diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationProvider.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/MigrationProvider.java rename to server-spi-private/src/main/java/org/keycloak/migration/MigrationProvider.java diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/MigrationProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/migration/MigrationProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/migration/MigrationSpi.java b/server-spi-private/src/main/java/org/keycloak/migration/MigrationSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/MigrationSpi.java rename to server-spi-private/src/main/java/org/keycloak/migration/MigrationSpi.java diff --git a/server-spi/src/main/java/org/keycloak/migration/ModelVersion.java b/server-spi-private/src/main/java/org/keycloak/migration/ModelVersion.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/ModelVersion.java rename to server-spi-private/src/main/java/org/keycloak/migration/ModelVersion.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_2_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_4_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_5_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_7_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_8_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo1_9_2.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_0_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_1_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_2_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrateTo2_3_0.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/Migration.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/Migration.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/Migration.java diff --git a/server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java b/server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java rename to server-spi-private/src/main/java/org/keycloak/migration/migrators/MigrationUtils.java diff --git a/server-spi/src/main/java/org/keycloak/models/AccountRoles.java b/server-spi-private/src/main/java/org/keycloak/models/AccountRoles.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/AccountRoles.java rename to server-spi-private/src/main/java/org/keycloak/models/AccountRoles.java diff --git a/server-spi/src/main/java/org/keycloak/models/AdminRoles.java b/server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/AdminRoles.java rename to server-spi-private/src/main/java/org/keycloak/models/AdminRoles.java diff --git a/server-spi/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java b/server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java rename to server-spi-private/src/main/java/org/keycloak/models/BrowserSecurityHeaders.java diff --git a/server-spi/src/main/java/org/keycloak/models/ClaimMask.java b/server-spi-private/src/main/java/org/keycloak/models/ClaimMask.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/ClaimMask.java rename to server-spi-private/src/main/java/org/keycloak/models/ClaimMask.java diff --git a/server-spi/src/main/java/org/keycloak/models/ClientConfigResolver.java b/server-spi-private/src/main/java/org/keycloak/models/ClientConfigResolver.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/ClientConfigResolver.java rename to server-spi-private/src/main/java/org/keycloak/models/ClientConfigResolver.java diff --git a/server-spi/src/main/java/org/keycloak/models/Constants.java b/server-spi-private/src/main/java/org/keycloak/models/Constants.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/Constants.java rename to server-spi-private/src/main/java/org/keycloak/models/Constants.java diff --git a/server-spi/src/main/java/org/keycloak/models/ImpersonationConstants.java b/server-spi-private/src/main/java/org/keycloak/models/ImpersonationConstants.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/ImpersonationConstants.java rename to server-spi-private/src/main/java/org/keycloak/models/ImpersonationConstants.java diff --git a/server-spi/src/main/java/org/keycloak/models/LDAPConstants.java b/server-spi-private/src/main/java/org/keycloak/models/LDAPConstants.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/LDAPConstants.java rename to server-spi-private/src/main/java/org/keycloak/models/LDAPConstants.java diff --git a/server-spi/src/main/java/org/keycloak/models/ModelDuplicateException.java b/server-spi-private/src/main/java/org/keycloak/models/ModelDuplicateException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/ModelDuplicateException.java rename to server-spi-private/src/main/java/org/keycloak/models/ModelDuplicateException.java diff --git a/server-spi/src/main/java/org/keycloak/models/ModelException.java b/server-spi-private/src/main/java/org/keycloak/models/ModelException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/ModelException.java rename to server-spi-private/src/main/java/org/keycloak/models/ModelException.java diff --git a/server-spi/src/main/java/org/keycloak/models/ModelReadOnlyException.java b/server-spi-private/src/main/java/org/keycloak/models/ModelReadOnlyException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/ModelReadOnlyException.java rename to server-spi-private/src/main/java/org/keycloak/models/ModelReadOnlyException.java diff --git a/server-spi/src/main/java/org/keycloak/models/RealmProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/RealmProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/RealmProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/RealmProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/RealmSpi.java b/server-spi-private/src/main/java/org/keycloak/models/RealmSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/RealmSpi.java rename to server-spi-private/src/main/java/org/keycloak/models/RealmSpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java b/server-spi-private/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java rename to server-spi-private/src/main/java/org/keycloak/models/UserFederationProviderCreationEventImpl.java diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationSpi.java b/server-spi-private/src/main/java/org/keycloak/models/UserFederationSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/UserFederationSpi.java rename to server-spi-private/src/main/java/org/keycloak/models/UserFederationSpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/UserFederationValidatingProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/UserProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/UserProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/UserProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/UserProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/UserSessionProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/UserSessionProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/UserSessionProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/UserSessionSpi.java b/server-spi-private/src/main/java/org/keycloak/models/UserSessionSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/UserSessionSpi.java rename to server-spi-private/src/main/java/org/keycloak/models/UserSessionSpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/UserSpi.java b/server-spi-private/src/main/java/org/keycloak/models/UserSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/UserSpi.java rename to server-spi-private/src/main/java/org/keycloak/models/UserSpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java b/server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProvider.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProviderSpi.java b/server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProviderSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/CacheRealmProviderSpi.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/CacheRealmProviderSpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java b/server-spi-private/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/CacheUserProviderSpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/CachedRealmModel.java b/server-spi-private/src/main/java/org/keycloak/models/cache/CachedRealmModel.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/CachedRealmModel.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/CachedRealmModel.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/UserCacheProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/cache/UserCacheProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/UserCacheProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/UserCacheProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactoryProvider.java b/server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactoryProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactoryProvider.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactoryProvider.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactorySpi.java b/server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactorySpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactorySpi.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreFactorySpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/cache/authorization/CachedStoreProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/dblock/DBLockManager.java b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/dblock/DBLockManager.java rename to server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockManager.java diff --git a/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/dblock/DBLockProvider.java rename to server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProvider.java diff --git a/server-spi/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/dblock/DBLockSpi.java b/server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/dblock/DBLockSpi.java rename to server-spi-private/src/main/java/org/keycloak/models/dblock/DBLockSpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java b/server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java rename to server-spi-private/src/main/java/org/keycloak/models/session/DisabledUserSessionPersisterProvider.java diff --git a/server-spi/src/main/java/org/keycloak/models/session/PersistentClientSessionAdapter.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionAdapter.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/session/PersistentClientSessionAdapter.java rename to server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionAdapter.java diff --git a/server-spi/src/main/java/org/keycloak/models/session/PersistentClientSessionModel.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionModel.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/session/PersistentClientSessionModel.java rename to server-spi-private/src/main/java/org/keycloak/models/session/PersistentClientSessionModel.java diff --git a/server-spi/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java rename to server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionAdapter.java diff --git a/server-spi/src/main/java/org/keycloak/models/session/PersistentUserSessionModel.java b/server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionModel.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/session/PersistentUserSessionModel.java rename to server-spi-private/src/main/java/org/keycloak/models/session/PersistentUserSessionModel.java diff --git a/server-spi/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/models/session/UserSessionPersisterSpi.java b/server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/session/UserSessionPersisterSpi.java rename to server-spi-private/src/main/java/org/keycloak/models/session/UserSessionPersisterSpi.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ComponentUtil.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ComponentUtil.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/ComponentUtil.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/ComponentUtil.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/CredentialValidation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/CredentialValidation.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/CredentialValidation.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/CredentialValidation.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/DefaultKeyProviders.java b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultKeyProviders.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/DefaultKeyProviders.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/DefaultKeyProviders.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/DefaultRequiredActions.java b/server-spi-private/src/main/java/org/keycloak/models/utils/DefaultRequiredActions.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/DefaultRequiredActions.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/DefaultRequiredActions.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/FormMessage.java b/server-spi-private/src/main/java/org/keycloak/models/utils/FormMessage.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/FormMessage.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/FormMessage.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java similarity index 90% rename from server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java index b57d2b1e62e..e26523366ee 100755 --- a/server-spi/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java @@ -257,75 +257,6 @@ public final class KeycloakModelUtils { return realmName + "-realm"; } - /** - * @param roles - * @param targetRole - * @return true if targetRole is in roles (directly or indirectly via composite role) - */ - public static boolean hasRole(Set roles, RoleModel targetRole) { - if (roles.contains(targetRole)) return true; - - for (RoleModel mapping : roles) { - if (mapping.hasRole(targetRole)) return true; - } - return false; - } - - /** - * Checks whether the {@code targetRole} is contained in the given group or its parents - * (if requested) - * @param group Group to check role for - * @param targetRole - * @param checkParentGroup When {@code true}, also parent group is recursively checked for role - * @return true if targetRole is in roles (directly or indirectly via composite role) - */ - public static boolean hasRoleFromGroup(GroupModel group, RoleModel targetRole, boolean checkParentGroup) { - if (group.hasRole(targetRole)) - return true; - - if (checkParentGroup) { - GroupModel parent = group.getParent(); - return parent != null && hasRoleFromGroup(parent, targetRole, true); - } - - return false; - } - - /** - * Checks whether the {@code targetRole} is contained in any of the {@code groups} or their parents - * (if requested) - * @param groups - * @param targetRole - * @param checkParentGroup When {@code true}, also parent group is recursively checked for role - * @return true if targetRole is in roles (directly or indirectly via composite role) - */ - public static boolean hasRoleFromGroup(Iterable groups, RoleModel targetRole, boolean checkParentGroup) { - if (groups == null) { - return false; - } - - return StreamSupport.stream(groups.spliterator(), false) - .anyMatch(group -> hasRoleFromGroup(group, targetRole, checkParentGroup)); - } - - /** - * - * @param groups - * @param targetGroup - * @return true if targetGroup is in groups (directly or indirectly via parent child relationship) - */ - public static boolean isMember(Set groups, GroupModel targetGroup) { - if (groups.contains(targetGroup)) return true; - - for (GroupModel mapping : groups) { - GroupModel child = mapping; - while(child.getParent() != null) { - if (child.getParent().equals(targetGroup)) return true; - child = child.getParent(); - } - } - return false; - } // USER FEDERATION RELATED STUFF /** diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/PostMigrationEvent.java b/server-spi-private/src/main/java/org/keycloak/models/utils/PostMigrationEvent.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/PostMigrationEvent.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/PostMigrationEvent.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RealmImporter.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RealmImporter.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/RealmImporter.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/RealmImporter.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RealmInfoUtil.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RealmInfoUtil.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/RealmInfoUtil.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/RealmInfoUtil.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/RepresentationToModel.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java b/server-spi-private/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/SHAPasswordEncoder.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/StripSecretsUtils.java b/server-spi-private/src/main/java/org/keycloak/models/utils/StripSecretsUtils.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/StripSecretsUtils.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/StripSecretsUtils.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java b/server-spi-private/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/TimeBasedOTP.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java b/server-spi-private/src/main/java/org/keycloak/models/utils/UserModelDelegate.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/UserModelDelegate.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/UserModelDelegate.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/AnnotatedPropertyCriteria.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/MethodProperty.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodProperty.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/MethodProperty.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodProperty.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/NamedPropertyCriteria.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/NamedPropertyCriteria.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/NamedPropertyCriteria.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/NamedPropertyCriteria.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/Properties.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Properties.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/Properties.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Properties.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/Property.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Property.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/Property.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/Property.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyCriteria.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/PropertyQueries.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQueries.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/PropertyQueries.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQueries.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/PropertyQuery.java diff --git a/server-spi/src/main/java/org/keycloak/models/utils/reflection/TypedPropertyCriteria.java b/server-spi-private/src/main/java/org/keycloak/models/utils/reflection/TypedPropertyCriteria.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/models/utils/reflection/TypedPropertyCriteria.java rename to server-spi-private/src/main/java/org/keycloak/models/utils/reflection/TypedPropertyCriteria.java diff --git a/server-spi/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/DefaultPasswordPolicyManagerProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/DigitsPasswordPolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java similarity index 96% rename from server-spi/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java index 114447834d0..b9f6f4ce414 100644 --- a/server-spi/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/policy/ForceExpiredPasswordPolicyProviderFactory.java @@ -20,6 +20,7 @@ package org.keycloak.policy; import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; @@ -28,7 +29,6 @@ import org.keycloak.models.UserModel; */ public class ForceExpiredPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory, PasswordPolicyProvider { - public static final String ID = "forceExpiredPasswordChange"; public static final int DEFAULT_VALUE = 365; @Override @@ -50,7 +50,7 @@ public class ForceExpiredPasswordPolicyProviderFactory implements PasswordPolicy @Override public String getId() { - return ID; + return PasswordPolicy.FORCE_EXPIRED_ID; } @Override diff --git a/server-spi/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java similarity index 90% rename from server-spi/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java index e7daaabba0a..303ba7993fa 100644 --- a/server-spi/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/policy/HashAlgorithmPasswordPolicyProviderFactory.java @@ -20,6 +20,7 @@ package org.keycloak.policy; import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; @@ -28,10 +29,6 @@ import org.keycloak.models.UserModel; */ public class HashAlgorithmPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory, PasswordPolicyProvider { - public static final String DEFAULT_VALUE = "pbkdf2"; - - public static final String ID = "hashAlgorithm"; - @Override public PasswordPolicyProvider create(KeycloakSession session) { return this; @@ -51,7 +48,7 @@ public class HashAlgorithmPasswordPolicyProviderFactory implements PasswordPolic @Override public String getId() { - return ID; + return PasswordPolicy.HASH_ALGORITHM_ID; } @Override @@ -76,7 +73,7 @@ public class HashAlgorithmPasswordPolicyProviderFactory implements PasswordPolic @Override public String getDefaultConfigValue() { - return DEFAULT_VALUE; + return PasswordPolicy.HASH_ALGORITHM_DEFAULT; } @Override @@ -86,7 +83,7 @@ public class HashAlgorithmPasswordPolicyProviderFactory implements PasswordPolic @Override public Object parseConfig(String value) { - return value != null ? value : DEFAULT_VALUE; + return value != null ? value : PasswordPolicy.HASH_ALGORITHM_DEFAULT; } } diff --git a/server-spi/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java similarity index 89% rename from server-spi/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java index ae9d497e238..695ab282ea6 100644 --- a/server-spi/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/policy/HashIterationsPasswordPolicyProviderFactory.java @@ -20,6 +20,7 @@ package org.keycloak.policy; import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; @@ -28,9 +29,6 @@ import org.keycloak.models.UserModel; */ public class HashIterationsPasswordPolicyProviderFactory implements PasswordPolicyProvider, PasswordPolicyProviderFactory { - public static final int DEFAULT_VALUE = 20000; - - public static final String ID = "hashIterations"; @Override public PasswordPolicyProvider create(KeycloakSession session) { @@ -47,7 +45,7 @@ public class HashIterationsPasswordPolicyProviderFactory implements PasswordPoli @Override public String getId() { - return ID; + return PasswordPolicy.HASH_ITERATIONS_ID; } @Override @@ -62,7 +60,7 @@ public class HashIterationsPasswordPolicyProviderFactory implements PasswordPoli @Override public Object parseConfig(String value) { - return value != null ? Integer.parseInt(value) : DEFAULT_VALUE; + return value != null ? Integer.parseInt(value) : PasswordPolicy.HASH_ITERATIONS_DEFAULT; } @Override @@ -77,7 +75,7 @@ public class HashIterationsPasswordPolicyProviderFactory implements PasswordPoli @Override public String getDefaultConfigValue() { - return String.valueOf(DEFAULT_VALUE); + return String.valueOf(PasswordPolicy.HASH_ITERATIONS_DEFAULT); } @Override diff --git a/server-spi/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java similarity index 98% rename from server-spi/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java index 154ea56b39d..004d54028ed 100644 --- a/server-spi/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java +++ b/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProvider.java @@ -49,7 +49,7 @@ public class HistoryPasswordPolicyProvider implements PasswordPolicyProvider { @Override public PolicyError validate(RealmModel realm, UserModel user, String password) { PasswordPolicy policy = session.getContext().getRealm().getPasswordPolicy(); - int passwordHistoryPolicyValue = policy.getPolicyConfig(HistoryPasswordPolicyProviderFactory.ID); + int passwordHistoryPolicyValue = policy.getPolicyConfig(PasswordPolicy.PASSWORD_HISTORY_ID); if (passwordHistoryPolicyValue != -1) { List storedPasswords = session.userCredentialManager().getStoredCredentialsByType(realm, user, CredentialModel.PASSWORD); for (CredentialModel cred : storedPasswords) { diff --git a/server-spi/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java similarity index 95% rename from server-spi/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java index c2c180a020a..5f8a9d18884 100644 --- a/server-spi/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java +++ b/server-spi-private/src/main/java/org/keycloak/policy/HistoryPasswordPolicyProviderFactory.java @@ -20,18 +20,18 @@ package org.keycloak.policy; import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.PasswordPolicy; /** * @author Stian Thorgersen */ public class HistoryPasswordPolicyProviderFactory implements PasswordPolicyProviderFactory { - public static final String ID = "passwordHistory"; public static final Integer DEFAULT_VALUE = 3; @Override public String getId() { - return ID; + return PasswordPolicy.PASSWORD_HISTORY_ID; } @Override diff --git a/server-spi/src/main/java/org/keycloak/policy/LengthPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/LengthPasswordPolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/LengthPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/LengthPasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/LengthPasswordPolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/LowerCasePasswordPolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/NotUsernamePasswordPolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/PasswordPolicyManagerProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/PasswordPolicyManagerProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/PasswordPolicyManagerProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/PasswordPolicyManagerProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/PasswordPolicyManagerSpi.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/PasswordPolicyManagerSpi.java rename to server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyManagerSpi.java diff --git a/server-spi/src/main/java/org/keycloak/policy/PasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/PasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/PasswordPolicySpi.java b/server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/PasswordPolicySpi.java rename to server-spi-private/src/main/java/org/keycloak/policy/PasswordPolicySpi.java diff --git a/server-spi/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/RegexPatternsPasswordPolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/SpecialCharsPasswordPolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProvider.java b/server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProvider.java rename to server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProvider.java diff --git a/server-spi/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/policy/UpperCasePasswordPolicyProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java rename to server-spi-private/src/main/java/org/keycloak/protocol/AbstractLoginProtocolFactory.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java b/server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java rename to server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationProvider.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java b/server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java rename to server-spi-private/src/main/java/org/keycloak/protocol/ClientInstallationSpi.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/LoginProtocol.java b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/LoginProtocol.java rename to server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocol.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java rename to server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java b/server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java rename to server-spi-private/src/main/java/org/keycloak/protocol/LoginProtocolSpi.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/ProtocolMapper.java b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapper.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/ProtocolMapper.java rename to server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapper.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java rename to server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperConfigException.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/ProtocolMapperSpi.java b/server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/ProtocolMapperSpi.java rename to server-spi-private/src/main/java/org/keycloak/protocol/ProtocolMapperSpi.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java rename to server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProvider.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java b/server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java rename to server-spi-private/src/main/java/org/keycloak/protocol/oidc/TokenIntrospectionSpi.java diff --git a/server-spi/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java b/server-spi-private/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java rename to server-spi-private/src/main/java/org/keycloak/provider/ConfigurationValidationHelper.java diff --git a/server-spi/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/provider/EnvironmentDependentProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/provider/ProviderLoader.java b/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoader.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/provider/ProviderLoader.java rename to server-spi-private/src/main/java/org/keycloak/provider/ProviderLoader.java diff --git a/server-spi/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java b/server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java rename to server-spi-private/src/main/java/org/keycloak/provider/ProviderLoaderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/provider/ServerInfoAwareProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/provider/ServerInfoAwareProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/provider/ServerInfoAwareProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/provider/ServerInfoAwareProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/scripting/InvocableScriptAdapter.java b/server-spi-private/src/main/java/org/keycloak/scripting/InvocableScriptAdapter.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/scripting/InvocableScriptAdapter.java rename to server-spi-private/src/main/java/org/keycloak/scripting/InvocableScriptAdapter.java diff --git a/server-spi/src/main/java/org/keycloak/scripting/Script.java b/server-spi-private/src/main/java/org/keycloak/scripting/Script.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/scripting/Script.java rename to server-spi-private/src/main/java/org/keycloak/scripting/Script.java diff --git a/server-spi/src/main/java/org/keycloak/scripting/ScriptBindingsConfigurer.java b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptBindingsConfigurer.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/scripting/ScriptBindingsConfigurer.java rename to server-spi-private/src/main/java/org/keycloak/scripting/ScriptBindingsConfigurer.java diff --git a/server-spi/src/main/java/org/keycloak/scripting/ScriptExecutionException.java b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptExecutionException.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/scripting/ScriptExecutionException.java rename to server-spi-private/src/main/java/org/keycloak/scripting/ScriptExecutionException.java diff --git a/server-spi/src/main/java/org/keycloak/scripting/ScriptingProvider.java b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptingProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/scripting/ScriptingProvider.java rename to server-spi-private/src/main/java/org/keycloak/scripting/ScriptingProvider.java diff --git a/server-spi/src/main/java/org/keycloak/scripting/ScriptingProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptingProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/scripting/ScriptingProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/scripting/ScriptingProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/scripting/ScriptingSpi.java b/server-spi-private/src/main/java/org/keycloak/scripting/ScriptingSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/scripting/ScriptingSpi.java rename to server-spi-private/src/main/java/org/keycloak/scripting/ScriptingSpi.java diff --git a/server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtector.java b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtector.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtector.java rename to server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtector.java diff --git a/server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtectorFactory.java b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtectorFactory.java rename to server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorFactory.java diff --git a/server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtectorSpi.java b/server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/services/managers/BruteForceProtectorSpi.java rename to server-spi-private/src/main/java/org/keycloak/services/managers/BruteForceProtectorSpi.java diff --git a/server-spi/src/main/java/org/keycloak/services/managers/ClientSessionCode.java b/server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/services/managers/ClientSessionCode.java rename to server-spi-private/src/main/java/org/keycloak/services/managers/ClientSessionCode.java diff --git a/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java rename to server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProvider.java diff --git a/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/services/resource/RealmResourceSPI.java b/server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceSPI.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/services/resource/RealmResourceSPI.java rename to server-spi-private/src/main/java/org/keycloak/services/resource/RealmResourceSPI.java diff --git a/server-spi/src/main/java/org/keycloak/theme/Theme.java b/server-spi-private/src/main/java/org/keycloak/theme/Theme.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/theme/Theme.java rename to server-spi-private/src/main/java/org/keycloak/theme/Theme.java diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeProvider.java b/server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/theme/ThemeProvider.java rename to server-spi-private/src/main/java/org/keycloak/theme/ThemeProvider.java diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/theme/ThemeProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/theme/ThemeProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/theme/ThemeProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/theme/ThemeSpi.java b/server-spi-private/src/main/java/org/keycloak/theme/ThemeSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/theme/ThemeSpi.java rename to server-spi-private/src/main/java/org/keycloak/theme/ThemeSpi.java diff --git a/server-spi/src/main/java/org/keycloak/timer/ScheduledTask.java b/server-spi-private/src/main/java/org/keycloak/timer/ScheduledTask.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/timer/ScheduledTask.java rename to server-spi-private/src/main/java/org/keycloak/timer/ScheduledTask.java diff --git a/server-spi/src/main/java/org/keycloak/timer/TimerProvider.java b/server-spi-private/src/main/java/org/keycloak/timer/TimerProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/timer/TimerProvider.java rename to server-spi-private/src/main/java/org/keycloak/timer/TimerProvider.java diff --git a/server-spi/src/main/java/org/keycloak/timer/TimerProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/timer/TimerProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/timer/TimerProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/timer/TimerProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/timer/TimerSpi.java b/server-spi-private/src/main/java/org/keycloak/timer/TimerSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/timer/TimerSpi.java rename to server-spi-private/src/main/java/org/keycloak/timer/TimerSpi.java diff --git a/server-spi/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java b/server-spi-private/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java rename to server-spi-private/src/main/java/org/keycloak/transaction/JtaTransactionManagerLookup.java diff --git a/server-spi/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java b/server-spi-private/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java rename to server-spi-private/src/main/java/org/keycloak/transaction/TransactionManagerLookupSpi.java diff --git a/server-spi/src/main/java/org/keycloak/truststore/HostnameVerificationPolicy.java b/server-spi-private/src/main/java/org/keycloak/truststore/HostnameVerificationPolicy.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/truststore/HostnameVerificationPolicy.java rename to server-spi-private/src/main/java/org/keycloak/truststore/HostnameVerificationPolicy.java diff --git a/server-spi/src/main/java/org/keycloak/truststore/TruststoreProvider.java b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProvider.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/truststore/TruststoreProvider.java rename to server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProvider.java diff --git a/server-spi/src/main/java/org/keycloak/truststore/TruststoreProviderFactory.java b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProviderFactory.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/truststore/TruststoreProviderFactory.java rename to server-spi-private/src/main/java/org/keycloak/truststore/TruststoreProviderFactory.java diff --git a/server-spi/src/main/java/org/keycloak/truststore/TruststoreSpi.java b/server-spi-private/src/main/java/org/keycloak/truststore/TruststoreSpi.java similarity index 100% rename from server-spi/src/main/java/org/keycloak/truststore/TruststoreSpi.java rename to server-spi-private/src/main/java/org/keycloak/truststore/TruststoreSpi.java diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory similarity index 100% rename from server-spi/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory rename to server-spi-private/src/main/resources/META-INF/services/org.keycloak.models.session.UserSessionPersisterProviderFactory diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory similarity index 100% rename from server-spi/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory rename to server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyManagerProviderFactory diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory similarity index 100% rename from server-spi/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory rename to server-spi-private/src/main/resources/META-INF/services/org.keycloak.policy.PasswordPolicyProviderFactory diff --git a/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100755 index 00000000000..bbd588ea50b --- /dev/null +++ b/server-spi-private/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1,68 @@ +# +# 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. +# + +org.keycloak.models.UserFederationSpi +org.keycloak.storage.UserStorageProviderSpi +org.keycloak.storage.federated.UserFederatedStorageProviderSpi +org.keycloak.mappers.UserFederationMapperSpi +org.keycloak.models.RealmSpi +org.keycloak.models.UserSessionSpi +org.keycloak.models.UserSpi +org.keycloak.models.session.UserSessionPersisterSpi +org.keycloak.models.dblock.DBLockSpi +org.keycloak.migration.MigrationSpi +org.keycloak.events.EventListenerSpi +org.keycloak.events.EventStoreSpi +org.keycloak.exportimport.ExportSpi +org.keycloak.exportimport.ImportSpi +org.keycloak.timer.TimerSpi +org.keycloak.scripting.ScriptingSpi +org.keycloak.services.managers.BruteForceProtectorSpi +org.keycloak.services.resource.RealmResourceSPI +org.keycloak.protocol.ClientInstallationSpi +org.keycloak.protocol.LoginProtocolSpi +org.keycloak.protocol.ProtocolMapperSpi +org.keycloak.broker.provider.IdentityProviderSpi +org.keycloak.broker.provider.IdentityProviderMapperSpi +org.keycloak.broker.social.SocialProviderSpi +org.keycloak.forms.account.AccountSpi +org.keycloak.forms.login.LoginFormsSpi +org.keycloak.email.EmailSenderSpi +org.keycloak.email.EmailTemplateSpi +org.keycloak.theme.ThemeSpi +org.keycloak.truststore.TruststoreSpi +org.keycloak.connections.httpclient.HttpClientSpi +org.keycloak.models.cache.CacheRealmProviderSpi +org.keycloak.models.cache.CacheUserProviderSpi +org.keycloak.authentication.AuthenticatorSpi +org.keycloak.authentication.ClientAuthenticatorSpi +org.keycloak.authentication.RequiredActionSpi +org.keycloak.authentication.FormAuthenticatorSpi +org.keycloak.authentication.FormActionSpi +org.keycloak.cluster.ClusterSpi +org.keycloak.authorization.policy.provider.PolicySpi +org.keycloak.authorization.store.StoreFactorySpi +org.keycloak.authorization.AuthorizationSpi +org.keycloak.models.cache.authorization.CachedStoreFactorySpi +org.keycloak.protocol.oidc.TokenIntrospectionSpi +org.keycloak.policy.PasswordPolicySpi +org.keycloak.policy.PasswordPolicyManagerSpi +org.keycloak.transaction.TransactionManagerLookupSpi +org.keycloak.credential.hash.PasswordHashSpi +org.keycloak.credential.CredentialSpi +org.keycloak.keys.PublicKeyStorageSpi +org.keycloak.keys.KeySpi \ No newline at end of file diff --git a/server-spi/src/test/java/org/keycloak/models/HmacTest.java b/server-spi-private/src/test/java/org/keycloak/models/HmacTest.java similarity index 100% rename from server-spi/src/test/java/org/keycloak/models/HmacTest.java rename to server-spi-private/src/test/java/org/keycloak/models/HmacTest.java diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java b/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java index e644a8095c9..be5f55189f9 100755 --- a/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java +++ b/server-spi/src/main/java/org/keycloak/models/KeycloakContext.java @@ -18,7 +18,6 @@ package org.keycloak.models; import org.keycloak.common.ClientConnection; -import org.keycloak.models.utils.RealmImporter; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.UriInfo; @@ -52,8 +51,6 @@ public interface KeycloakContext { void setConnection(ClientConnection connection); - RealmImporter getRealmManager(); - Locale resolveLocale(UserModel user); } diff --git a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java index cb1089192d1..f3242eb73b5 100755 --- a/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java +++ b/server-spi/src/main/java/org/keycloak/models/KeycloakSession.java @@ -20,7 +20,6 @@ package org.keycloak.models; import org.keycloak.component.ComponentModel; import org.keycloak.models.cache.UserCache; import org.keycloak.provider.Provider; -import org.keycloak.scripting.ScriptingProvider; import org.keycloak.storage.federated.UserFederatedStorageProvider; import java.util.Set; @@ -157,8 +156,4 @@ public interface KeycloakSession { */ KeyManager keys(); - /** - * Keycloak scripting support. - */ - ScriptingProvider scripting(); } diff --git a/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java b/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java index 95dfc5301e5..8833c8a33c2 100755 --- a/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java +++ b/server-spi/src/main/java/org/keycloak/models/PasswordPolicy.java @@ -17,10 +17,6 @@ package org.keycloak.models; -import org.keycloak.policy.ForceExpiredPasswordPolicyProviderFactory; -import org.keycloak.policy.HashAlgorithmPasswordPolicyProviderFactory; -import org.keycloak.policy.HashIterationsPasswordPolicyProviderFactory; -import org.keycloak.policy.HistoryPasswordPolicyProviderFactory; import org.keycloak.policy.PasswordPolicyProvider; import java.io.Serializable; @@ -33,6 +29,18 @@ import java.util.Set; */ public class PasswordPolicy implements Serializable { + public static final String HASH_ALGORITHM_ID = "hashAlgorithm"; + + public static final String HASH_ALGORITHM_DEFAULT = "pbkdf2"; + + public static final String HASH_ITERATIONS_ID = "hashIterations"; + + public static final int HASH_ITERATIONS_DEFAULT = 20000; + + public static final String PASSWORD_HISTORY_ID = "passwordHistory"; + + public static final String FORCE_EXPIRED_ID = "forceExpiredPasswordChange"; + private String policyString; private Map policyConfig; @@ -84,32 +92,32 @@ public class PasswordPolicy implements Serializable { } public String getHashAlgorithm() { - if (policyConfig.containsKey(HashAlgorithmPasswordPolicyProviderFactory.ID)) { - return getPolicyConfig(HashAlgorithmPasswordPolicyProviderFactory.ID); + if (policyConfig.containsKey(HASH_ALGORITHM_ID)) { + return getPolicyConfig(HASH_ALGORITHM_ID); } else { - return HashAlgorithmPasswordPolicyProviderFactory.DEFAULT_VALUE; + return HASH_ALGORITHM_DEFAULT; } } public int getHashIterations() { - if (policyConfig.containsKey(HashIterationsPasswordPolicyProviderFactory.ID)) { - return getPolicyConfig(HashIterationsPasswordPolicyProviderFactory.ID); + if (policyConfig.containsKey(HASH_ITERATIONS_ID)) { + return getPolicyConfig(HASH_ITERATIONS_ID); } else { - return HashIterationsPasswordPolicyProviderFactory.DEFAULT_VALUE; + return HASH_ITERATIONS_DEFAULT; } } public int getExpiredPasswords() { - if (policyConfig.containsKey(HistoryPasswordPolicyProviderFactory.ID)) { - return getPolicyConfig(HistoryPasswordPolicyProviderFactory.ID); + if (policyConfig.containsKey(PASSWORD_HISTORY_ID)) { + return getPolicyConfig(PASSWORD_HISTORY_ID); } else { return -1; } } public int getDaysToExpirePassword() { - if (policyConfig.containsKey(ForceExpiredPasswordPolicyProviderFactory.ID)) { - return getPolicyConfig(ForceExpiredPasswordPolicyProviderFactory.ID); + if (policyConfig.containsKey(FORCE_EXPIRED_ID)) { + return getPolicyConfig(FORCE_EXPIRED_ID); } else { return -1; } diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java index 1765bad2092..707b96ae3da 100755 --- a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java +++ b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java @@ -19,8 +19,6 @@ package org.keycloak.models; import org.jboss.logging.Logger; import org.keycloak.component.ComponentModel; -import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.services.managers.UserManager; import java.util.ArrayList; import java.util.Collections; @@ -74,7 +72,8 @@ public class UserFederationManager implements UserProvider { } public UserFederationProvider getFederationProvider(UserFederationProviderModel model) { - return KeycloakModelUtils.getFederationProviderInstance(session, model); + UserFederationProviderFactory factory = (UserFederationProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, model.getProviderName()); + return factory.getInstance(session, model); } public UserFederationProvider getFederationLink(RealmModel realm, UserModel user) { @@ -122,7 +121,7 @@ public class UserFederationManager implements UserProvider { } protected void deleteInvalidUser(final RealmModel realm, final UserModel user) { - KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() { + runJobInTransaction(session.getKeycloakSessionFactory(), new KeycloakSessionTask() { @Override public void run(KeycloakSession session) { @@ -136,6 +135,29 @@ public class UserFederationManager implements UserProvider { }); } + private static void runJobInTransaction(KeycloakSessionFactory factory, KeycloakSessionTask task) { + KeycloakSession session = factory.create(); + KeycloakTransaction tx = session.getTransactionManager(); + try { + tx.begin(); + task.run(session); + + if (tx.isActive()) { + if (tx.getRollbackOnly()) { + tx.rollback(); + } else { + tx.commit(); + } + } + } catch (RuntimeException re) { + if (tx.isActive()) { + tx.rollback(); + } + throw re; + } finally { + session.close(); + } + } protected UserModel validateAndProxyUser(RealmModel realm, UserModel user) { UserModel managed = managedUsers.get(user.getId()); diff --git a/server-spi/src/main/java/org/keycloak/services/managers/UserManager.java b/server-spi/src/main/java/org/keycloak/models/UserManager.java similarity index 87% rename from server-spi/src/main/java/org/keycloak/services/managers/UserManager.java rename to server-spi/src/main/java/org/keycloak/models/UserManager.java index d0e894782fc..81b2b51a98b 100755 --- a/server-spi/src/main/java/org/keycloak/services/managers/UserManager.java +++ b/server-spi/src/main/java/org/keycloak/models/UserManager.java @@ -15,13 +15,8 @@ * limitations under the License. */ -package org.keycloak.services.managers; +package org.keycloak.models; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; -import org.keycloak.models.UserProvider; -import org.keycloak.models.UserSessionProvider; import org.keycloak.models.session.UserSessionPersisterProvider; /** diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java b/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java new file mode 100644 index 00000000000..a03a55d9f0a --- /dev/null +++ b/server-spi/src/main/java/org/keycloak/models/utils/RoleUtils.java @@ -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.models.utils; + +import org.keycloak.models.GroupModel; +import org.keycloak.models.RoleModel; + +import java.util.Set; +import java.util.stream.StreamSupport; + +/** + * @author Stian Thorgersen + */ +public class RoleUtils { + + /** + * + * @param groups + * @param targetGroup + * @return true if targetGroup is in groups (directly or indirectly via parent child relationship) + */ + public static boolean isMember(Set groups, GroupModel targetGroup) { + if (groups.contains(targetGroup)) return true; + + for (GroupModel mapping : groups) { + GroupModel child = mapping; + while(child.getParent() != null) { + if (child.getParent().equals(targetGroup)) return true; + child = child.getParent(); + } + } + return false; + } + + /** + * @param roles + * @param targetRole + * @return true if targetRole is in roles (directly or indirectly via composite role) + */ + public static boolean hasRole(Set roles, RoleModel targetRole) { + if (roles.contains(targetRole)) return true; + + for (RoleModel mapping : roles) { + if (mapping.hasRole(targetRole)) return true; + } + return false; + } + + /** + * Checks whether the {@code targetRole} is contained in the given group or its parents + * (if requested) + * @param group Group to check role for + * @param targetRole + * @param checkParentGroup When {@code true}, also parent group is recursively checked for role + * @return true if targetRole is in roles (directly or indirectly via composite role) + */ + public static boolean hasRoleFromGroup(GroupModel group, RoleModel targetRole, boolean checkParentGroup) { + if (group.hasRole(targetRole)) + return true; + + if (checkParentGroup) { + GroupModel parent = group.getParent(); + return parent != null && hasRoleFromGroup(parent, targetRole, true); + } + + return false; + } + + /** + * Checks whether the {@code targetRole} is contained in any of the {@code groups} or their parents + * (if requested) + * @param groups + * @param targetRole + * @param checkParentGroup When {@code true}, also parent group is recursively checked for role + * @return true if targetRole is in roles (directly or indirectly via composite role) + */ + public static boolean hasRoleFromGroup(Iterable groups, RoleModel targetRole, boolean checkParentGroup) { + if (groups == null) { + return false; + } + + return StreamSupport.stream(groups.spliterator(), false) + .anyMatch(group -> hasRoleFromGroup(group, targetRole, checkParentGroup)); + } + +} diff --git a/server-spi/src/main/java/org/keycloak/provider/Spi.java b/server-spi/src/main/java/org/keycloak/provider/Spi.java index b9c47f8e5cb..8043bcf2e0c 100644 --- a/server-spi/src/main/java/org/keycloak/provider/Spi.java +++ b/server-spi/src/main/java/org/keycloak/provider/Spi.java @@ -17,9 +17,6 @@ package org.keycloak.provider; -import java.util.Collections; -import java.util.List; - /** * @author Stian Thorgersen */ diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java index df6c37ab2ab..c15902090e9 100644 --- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java +++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapter.java @@ -26,7 +26,7 @@ import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.DefaultRoles; -import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RoleUtils; import org.keycloak.storage.StorageId; import java.util.Collections; @@ -135,7 +135,7 @@ public abstract class AbstractUserAdapter implements UserModel { @Override public boolean isMemberOf(GroupModel group) { Set roles = getGroups(); - return KeycloakModelUtils.isMember(roles, group); + return RoleUtils.isMember(roles, group); } @Override @@ -172,8 +172,8 @@ public abstract class AbstractUserAdapter implements UserModel { @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role) - || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); + return RoleUtils.hasRole(roles, role) + || RoleUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java index 865f454e2cf..c93dac49b2b 100644 --- a/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java +++ b/server-spi/src/main/java/org/keycloak/storage/adapter/AbstractUserAdapterFederatedStorage.java @@ -25,7 +25,7 @@ import org.keycloak.models.RoleContainerModel; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.DefaultRoles; -import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.models.utils.RoleUtils; import org.keycloak.storage.StorageId; import org.keycloak.storage.federated.UserFederatedStorageProvider; @@ -140,7 +140,7 @@ public abstract class AbstractUserAdapterFederatedStorage implements UserModel { @Override public boolean isMemberOf(GroupModel group) { Set roles = getGroups(); - return KeycloakModelUtils.isMember(roles, group); + return RoleUtils.isMember(roles, group); } @Override @@ -177,8 +177,8 @@ public abstract class AbstractUserAdapterFederatedStorage implements UserModel { @Override public boolean hasRole(RoleModel role) { Set roles = getRoleMappings(); - return KeycloakModelUtils.hasRole(roles, role) - || KeycloakModelUtils.hasRoleFromGroup(getGroups(), role, true); + return RoleUtils.hasRole(roles, role) + || RoleUtils.hasRoleFromGroup(getGroups(), role, true); } @Override diff --git a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi index bbd588ea50b..c7ee4865c70 100755 --- a/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi +++ b/server-spi/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -15,54 +15,21 @@ # limitations under the License. # -org.keycloak.models.UserFederationSpi -org.keycloak.storage.UserStorageProviderSpi -org.keycloak.storage.federated.UserFederatedStorageProviderSpi -org.keycloak.mappers.UserFederationMapperSpi -org.keycloak.models.RealmSpi -org.keycloak.models.UserSessionSpi -org.keycloak.models.UserSpi -org.keycloak.models.session.UserSessionPersisterSpi -org.keycloak.models.dblock.DBLockSpi -org.keycloak.migration.MigrationSpi -org.keycloak.events.EventListenerSpi -org.keycloak.events.EventStoreSpi -org.keycloak.exportimport.ExportSpi -org.keycloak.exportimport.ImportSpi -org.keycloak.timer.TimerSpi -org.keycloak.scripting.ScriptingSpi -org.keycloak.services.managers.BruteForceProtectorSpi -org.keycloak.services.resource.RealmResourceSPI -org.keycloak.protocol.ClientInstallationSpi -org.keycloak.protocol.LoginProtocolSpi -org.keycloak.protocol.ProtocolMapperSpi -org.keycloak.broker.provider.IdentityProviderSpi -org.keycloak.broker.provider.IdentityProviderMapperSpi -org.keycloak.broker.social.SocialProviderSpi -org.keycloak.forms.account.AccountSpi -org.keycloak.forms.login.LoginFormsSpi -org.keycloak.email.EmailSenderSpi -org.keycloak.email.EmailTemplateSpi -org.keycloak.theme.ThemeSpi -org.keycloak.truststore.TruststoreSpi -org.keycloak.connections.httpclient.HttpClientSpi -org.keycloak.models.cache.CacheRealmProviderSpi -org.keycloak.models.cache.CacheUserProviderSpi -org.keycloak.authentication.AuthenticatorSpi -org.keycloak.authentication.ClientAuthenticatorSpi -org.keycloak.authentication.RequiredActionSpi -org.keycloak.authentication.FormAuthenticatorSpi -org.keycloak.authentication.FormActionSpi -org.keycloak.cluster.ClusterSpi -org.keycloak.authorization.policy.provider.PolicySpi -org.keycloak.authorization.store.StoreFactorySpi -org.keycloak.authorization.AuthorizationSpi -org.keycloak.models.cache.authorization.CachedStoreFactorySpi -org.keycloak.protocol.oidc.TokenIntrospectionSpi -org.keycloak.policy.PasswordPolicySpi -org.keycloak.policy.PasswordPolicyManagerSpi -org.keycloak.transaction.TransactionManagerLookupSpi -org.keycloak.credential.hash.PasswordHashSpi -org.keycloak.credential.CredentialSpi -org.keycloak.keys.PublicKeyStorageSpi -org.keycloak.keys.KeySpi \ No newline at end of file +# +# 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. +# + +org.keycloak.storage.UserStorageProviderSpi \ No newline at end of file diff --git a/services/pom.xml b/services/pom.xml index 78e3f42dcbf..f6fdb74cc7e 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -63,6 +63,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.keycloak keycloak-ldap-federation diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java index fdaa48187a5..ef17476e339 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ConditionalOtpFormAuthenticator.java @@ -20,6 +20,7 @@ package org.keycloak.authentication.authenticators.browser; import org.keycloak.authentication.AuthenticationFlowContext; import org.keycloak.models.RoleModel; import org.keycloak.models.UserModel; +import org.keycloak.models.utils.RoleUtils; import javax.ws.rs.core.MultivaluedMap; import java.util.List; @@ -30,7 +31,6 @@ import static org.keycloak.authentication.authenticators.browser.ConditionalOtpF import static org.keycloak.authentication.authenticators.browser.ConditionalOtpFormAuthenticator.OtpDecision.SHOW_OTP; import static org.keycloak.authentication.authenticators.browser.ConditionalOtpFormAuthenticator.OtpDecision.SKIP_OTP; import static org.keycloak.models.utils.KeycloakModelUtils.getRoleFromString; -import static org.keycloak.models.utils.KeycloakModelUtils.hasRole; /** * An {@link OTPFormAuthenticator} that can conditionally require OTP authentication. @@ -264,6 +264,6 @@ public class ConditionalOtpFormAuthenticator extends OTPFormAuthenticator { RoleModel role = getRoleFromString(context.getRealm(), roleName); UserModel user = context.getUser(); - return hasRole(user.getRoleMappings(), role); + return RoleUtils.hasRole(user.getRoleMappings(), role); } } diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java index 85a217ff1e5..9bff3f99982 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/ScriptBasedAuthenticator.java @@ -142,7 +142,7 @@ public class ScriptBasedAuthenticator implements Authenticator { RealmModel realm = context.getRealm(); - ScriptingProvider scripting = context.getSession().scripting(); + ScriptingProvider scripting = context.getSession().getProvider(ScriptingProvider.class); //TODO lookup script by scriptId instead of creating it every time ScriptModel script = scripting.createScript(realm.getId(), ScriptModel.TEXT_JAVASCRIPT, scriptName, scriptCode, scriptDescription); diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java index 997bc9368ee..0ef527629dc 100755 --- a/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java +++ b/services/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java @@ -73,9 +73,13 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import java.io.IOException; -import java.security.PublicKey; +import java.security.Key; import java.security.cert.X509Certificate; +import java.util.LinkedList; import java.util.List; +import org.keycloak.rotation.HardcodedKeyLocator; +import org.keycloak.rotation.KeyLocator; +import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; /** * @author Bill Burke @@ -174,14 +178,20 @@ public class SAMLEndpoint { protected abstract void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException; protected abstract SAMLDocumentHolder extractRequestDocument(String samlRequest); protected abstract SAMLDocumentHolder extractResponseDocument(String response); - protected PublicKey getIDPKey() { - X509Certificate certificate = null; - try { - certificate = XMLSignatureUtil.getX509CertificateFromKeyInfoString(config.getSigningCertificate().replaceAll("\\s", "")); - } catch (ProcessingException e) { - throw new RuntimeException(e); + + protected KeyLocator getIDPKeyLocator() { + List keys = new LinkedList<>(); + + for (String signingCertificate : config.getSigningCertificates()) { + try { + X509Certificate cert = XMLSignatureUtil.getX509CertificateFromKeyInfoString(signingCertificate.replaceAll("\\s", "")); + keys.add(cert.getPublicKey()); + } catch (ProcessingException e) { + throw new RuntimeException(e); + } } - return certificate.getPublicKey(); + + return new HardcodedKeyLocator(keys); } public Response execute(String samlRequest, String samlResponse, String relayState) { @@ -265,14 +275,18 @@ public class SAMLEndpoint { builder.issuer(issuerURL); JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder() .relayState(relayState); + boolean postBinding = config.isPostBindingResponse(); if (config.isWantAuthnRequestsSigned()) { KeyManager.ActiveKey keys = session.keys().getActiveKey(realm); - binding.signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()) + binding.signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()) .signatureAlgorithm(provider.getSignatureAlgorithm()) .signDocument(); + if (! postBinding && config.isAddExtensionsElementWithKeyInfo()) { // Only include extension if REDIRECT binding and signing whole SAML protocol message + builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid())); + } } try { - if (config.isPostBindingResponse()) { + if (postBinding) { return binding.postBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl()); } else { return binding.redirectBinding(builder.buildDocument()).response(config.getSingleLogoutServiceUrl()); @@ -418,7 +432,7 @@ public class SAMLEndpoint { protected class PostBinding extends Binding { @Override protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException { - SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKey()); + SamlProtocolUtils.verifyDocumentSignature(documentHolder.getSamlDocument(), getIDPKeyLocator()); } @Override @@ -440,8 +454,8 @@ public class SAMLEndpoint { protected class RedirectBinding extends Binding { @Override protected void verifySignature(String key, SAMLDocumentHolder documentHolder) throws VerificationException { - PublicKey publicKey = getIDPKey(); - SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, key); + KeyLocator locator = getIDPKeyLocator(); + SamlProtocolUtils.verifyRedirectSignature(documentHolder, locator, uriInfo, key); } diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java index 104b8f81ca9..f96f15a07f6 100755 --- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java +++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java @@ -50,8 +50,11 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import javax.ws.rs.core.UriInfo; import java.security.KeyPair; -import java.security.PrivateKey; -import java.security.PublicKey; +import java.util.Set; +import java.util.TreeSet; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; +import org.keycloak.keys.KeyMetadata; +import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; /** * @author Pedro Igor @@ -97,18 +100,22 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list + ? (int) (o2.getProviderPriority() - o1.getProviderPriority()) + : (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1)); + keys.addAll(session.keys().getKeys(realm, false)); + for (KeyMetadata key : keys) { + addKeyInfo(keysString, key, KeyTypes.SIGNING.value()); + } + String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, entityId, nameIDPolicyFormat, keysString.toString()); return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build(); } + private static void addKeyInfo(StringBuilder target, KeyMetadata key, String purpose) { + if (key == null) { + return; + } + + target.append(SPMetadataDescriptor.xmlKeyInfo(" ", key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, true)); + } + public SignatureAlgorithm getSignatureAlgorithm() { String alg = getConfig().getSignatureAlgorithm(); if (alg != null) { diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java index 1b2fb67da42..59b46ca9ca6 100755 --- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java +++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java @@ -62,14 +62,45 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel { getConfig().put("forceAuthn", String.valueOf(forceAuthn)); } + /** + * @deprecated Prefer {@link #getSigningCertificates()}} + * @param signingCertificate + */ public String getSigningCertificate() { - return getConfig().get("signingCertificate"); + return getConfig().get(SIGNING_CERTIFICATE_KEY); } + /** + * @deprecated Prefer {@link #addSigningCertificate(String)}} + * @param signingCertificate + */ public void setSigningCertificate(String signingCertificate) { - getConfig().put("signingCertificate", signingCertificate); + getConfig().put(SIGNING_CERTIFICATE_KEY, signingCertificate); } + public void addSigningCertificate(String signingCertificate) { + String crt = getConfig().get(SIGNING_CERTIFICATE_KEY); + if (crt == null || crt.isEmpty()) { + getConfig().put(SIGNING_CERTIFICATE_KEY, signingCertificate); + } else { + // Note that "," is not coding character per PEM format specification: + // see https://tools.ietf.org/html/rfc1421, section 4.3.2.4 Step 4: Printable Encoding + getConfig().put(SIGNING_CERTIFICATE_KEY, crt + "," + signingCertificate); + } + } + + public String[] getSigningCertificates() { + String crt = getConfig().get(SIGNING_CERTIFICATE_KEY); + if (crt == null || crt.isEmpty()) { + return new String[] { }; + } + // Note that "," is not coding character per PEM format specification: + // see https://tools.ietf.org/html/rfc1421, section 4.3.2.4 Step 4: Printable Encoding + return crt.split(","); + } + + public static final String SIGNING_CERTIFICATE_KEY = "signingCertificate"; + public String getNameIDPolicyFormat() { return getConfig().get("nameIDPolicyFormat"); } @@ -86,6 +117,14 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel { getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned)); } + public boolean isAddExtensionsElementWithKeyInfo() { + return Boolean.valueOf(getConfig().get("addExtensionsElementWithKeyInfo")); + } + + public void setAddExtensionsElementWithKeyInfo(boolean addExtensionsElementWithKeyInfo) { + getConfig().put("addExtensionsElementWithKeyInfo", String.valueOf(addExtensionsElementWithKeyInfo)); + } + public String getSignatureAlgorithm() { return getConfig().get("signatureAlgorithm"); } diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java index 714c47eebb8..0cc72da4c1e 100755 --- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java +++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderFactory.java @@ -108,6 +108,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory samlIdentityProviderConfig.setSingleLogoutServiceUrl(singleLogoutServiceUrl); samlIdentityProviderConfig.setSingleSignOnServiceUrl(singleSignOnServiceUrl); samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned()); + samlIdentityProviderConfig.setAddExtensionsElementWithKeyInfo(false); samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned()); samlIdentityProviderConfig.setPostBindingResponse(postBinding); samlIdentityProviderConfig.setPostBindingAuthnRequest(postBinding); @@ -121,7 +122,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory Element x509KeyInfo = DocumentUtil.getChildElement(keyInfo, new QName("dsig", "X509Certificate")); if (KeyTypes.SIGNING.equals(keyDescriptorType.getUse())) { - samlIdentityProviderConfig.setSigningCertificate(x509KeyInfo.getTextContent()); + samlIdentityProviderConfig.addSigningCertificate(x509KeyInfo.getTextContent()); } else if (KeyTypes.ENCRYPTION.equals(keyDescriptorType.getUse())) { samlIdentityProviderConfig.setEncryptionPublicKey(x509KeyInfo.getTextContent()); } else if (keyDescriptorType.getUse() == null) { @@ -131,8 +132,8 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory } if (defaultCertificate != null) { - if (samlIdentityProviderConfig.getSigningCertificate() == null) { - samlIdentityProviderConfig.setSigningCertificate(defaultCertificate); + if (samlIdentityProviderConfig.getSigningCertificates().length == 0) { + samlIdentityProviderConfig.addSigningCertificate(defaultCertificate); } if (samlIdentityProviderConfig.getEncryptionPublicKey() == null) { diff --git a/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java b/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java index 84cd0f06a5b..bdc32e70b3c 100644 --- a/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java +++ b/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java @@ -140,7 +140,7 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, policy.getHashAlgorithm()); if (hash == null) { logger.warnv("Realm PasswordPolicy PasswordHashProvider {0} not found", policy.getHashAlgorithm()); - return session.getProvider(PasswordHashProvider.class, HashAlgorithmPasswordPolicyProviderFactory.DEFAULT_VALUE); + return session.getProvider(PasswordHashProvider.class, PasswordPolicy.HASH_ALGORITHM_DEFAULT); } return hash; } diff --git a/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java index 1ea409288e8..2357a65df96 100755 --- a/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java +++ b/services/src/main/java/org/keycloak/exportimport/util/ImportUtils.java @@ -108,7 +108,8 @@ public class ImportUtils { } } - RealmImporter realmManager = session.getContext().getRealmManager(); + RealmManager realmManager = new RealmManager(session); + realmManager.setContextPath(session.getContext().getContextPath()); realmManager.importRealm(rep); if (System.getProperty(ExportImportConfig.ACTION) != null) { diff --git a/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java b/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java index c5f09f16b2c..f6bbaeb1838 100644 --- a/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/AbstractRsaKeyProvider.java @@ -23,7 +23,6 @@ import org.keycloak.models.RealmModel; import java.security.KeyPair; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.List; diff --git a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProvider.java b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProvider.java index d7fa87544d0..8c98bb4556b 100644 --- a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProvider.java +++ b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProvider.java @@ -24,11 +24,17 @@ import org.keycloak.component.ComponentModel; import org.keycloak.models.RealmModel; import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; import java.security.KeyPair; import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; +import java.security.UnrecoverableKeyException; import java.security.cert.Certificate; +import java.security.cert.CertificateException; import java.security.cert.X509Certificate; /** @@ -61,8 +67,18 @@ public class JavaKeystoreKeyProvider extends AbstractRsaKeyProvider { String kid = KeyUtils.createKeyId(keyPair.getPublic()); return new Keys(kid, keyPair, certificate); - } catch (Exception e) { - throw new RuntimeException("Failed to load keys", e); + } catch (KeyStoreException kse) { + throw new RuntimeException("KeyStore error on server. " + kse.getMessage(), kse); + } catch (FileNotFoundException fnfe) { + throw new RuntimeException("File not found on server. " + fnfe.getMessage(), fnfe); + } catch (IOException ioe) { + throw new RuntimeException("IO error on server. " + ioe.getMessage(), ioe); + } catch (NoSuchAlgorithmException nsae) { + throw new RuntimeException("Algorithm not available on server. " + nsae.getMessage(), nsae); + } catch (CertificateException ce) { + throw new RuntimeException("Certificate error on server. " + ce.getMessage(), ce); + } catch (UnrecoverableKeyException uke) { + throw new RuntimeException("Keystore on server can not be recovered. " + uke.getMessage(), uke); } } diff --git a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java index e6ed9848dd7..51b726a57b3 100644 --- a/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java +++ b/services/src/main/java/org/keycloak/keys/JavaKeystoreKeyProviderFactory.java @@ -27,6 +27,7 @@ import org.keycloak.provider.ConfigurationValidationHelper; import org.keycloak.provider.ProviderConfigProperty; import java.util.List; +import org.jboss.logging.Logger; import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE; @@ -34,6 +35,7 @@ import static org.keycloak.provider.ProviderConfigProperty.STRING_TYPE; * @author Stian Thorgersen */ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactory { + private static final Logger logger = Logger.getLogger(JavaKeystoreKeyProviderFactory.class); public static final String ID = "java-keystore"; @@ -77,7 +79,8 @@ public class JavaKeystoreKeyProviderFactory extends AbstractRsaKeyProviderFactor new JavaKeystoreKeyProvider(session.getContext().getRealm(), model) .loadKeys(session.getContext().getRealm(), model); } catch (Throwable t) { - throw new ComponentValidationException("Failed to load keys", t); + logger.error("Failed to load keys.", t); + throw new ComponentValidationException("Failed to load keys. " + t.getMessage(), t); } } diff --git a/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java b/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java index 0128e4e6683..cce0fec4da5 100755 --- a/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java +++ b/services/src/main/java/org/keycloak/partialimport/UsersPartialImport.java @@ -24,7 +24,7 @@ import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.representations.idm.PartialImportRepresentation; import org.keycloak.representations.idm.UserRepresentation; -import org.keycloak.services.managers.UserManager; +import org.keycloak.models.UserManager; import java.util.HashMap; import java.util.List; diff --git a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java index f00b2a766dd..60f5493c22d 100644 --- a/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java +++ b/services/src/main/java/org/keycloak/protocol/oidc/utils/RedirectUtils.java @@ -42,8 +42,9 @@ public class RedirectUtils { } public static String verifyRedirectUri(UriInfo uriInfo, String redirectUri, RealmModel realm, ClientModel client) { - Set validRedirects = client.getRedirectUris(); - return verifyRedirectUri(uriInfo, client.getRootUrl(), redirectUri, realm, validRedirects); + if (client != null) + return verifyRedirectUri(uriInfo, client.getRootUrl(), redirectUri, realm, client.getRedirectUris()); + return null; } public static Set resolveValidRedirects(UriInfo uriInfo, String rootUrl, Set validRedirects) { diff --git a/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java b/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java index 00caa116e5b..3d62a27eaed 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java +++ b/services/src/main/java/org/keycloak/protocol/saml/EntityDescriptorDescriptionConverter.java @@ -101,6 +101,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo app.setFullScopeAllowed(true); app.setProtocol(SamlProtocol.LOGIN_PROTOCOL); attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE, SamlProtocol.ATTRIBUTE_TRUE_VALUE); // default to true + attributes.put(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, SamlProtocol.ATTRIBUTE_FALSE_VALUE); // default to false attributes.put(SamlConfigAttributes.SAML_SIGNATURE_ALGORITHM, SignatureAlgorithm.RSA_SHA256.toString()); attributes.put(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE); SPSSODescriptorType spDescriptorType = CoreConfigUtil.getSPDescriptor(entity); @@ -110,7 +111,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo String logoutPost = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()); if (logoutPost != null) attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE, logoutPost); String logoutRedirect = getLogoutLocation(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get()); - if (logoutPost != null) attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, logoutRedirect); + if (logoutRedirect != null) attributes.put(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_REDIRECT_ATTRIBUTE, logoutRedirect); String assertionConsumerServicePostBinding = CoreConfigUtil.getServiceURL(spDescriptorType, JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get()); if (assertionConsumerServicePostBinding != null) { diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlClient.java b/services/src/main/java/org/keycloak/protocol/saml/SamlClient.java index 0415a72d77b..ee5aabadf04 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlClient.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlClient.java @@ -23,6 +23,8 @@ import org.keycloak.saml.SignatureAlgorithm; import org.keycloak.saml.common.constants.JBossSAMLURIConstants; /** + * Configuration of a SAML-enabled client. + * * @author Bill Burke * @version $Revision: 1 $ */ @@ -116,7 +118,14 @@ public class SamlClient extends ClientConfigResolver { public void setRequiresRealmSignature(boolean val) { client.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val)); + } + public boolean addExtensionsElementWithKeyInfo() { + return "true".equals(resolveAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT)); + } + + public void setAddExtensionsElementWithKeyInfo(boolean val) { + client.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, Boolean.toString(val)); } public boolean forcePostBinding() { diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java b/services/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java index e5bc2fa4e3c..0af3be07506 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlClientTemplate.java @@ -17,6 +17,7 @@ package org.keycloak.protocol.saml; +import java.util.Objects; import org.keycloak.models.ClientTemplateModel; import org.keycloak.saml.SignatureAlgorithm; @@ -89,7 +90,14 @@ public class SamlClientTemplate { public void setRequiresRealmSignature(boolean val) { clientTemplate.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE, Boolean.toString(val)); + } + public boolean addExtensionsElementWithKeyInfo() { + return Objects.equals("true", clientTemplate.getAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT)); + } + + public void setAddExtensionsElementWithKeyInfo(boolean val) { + clientTemplate.setAttribute(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT, Boolean.toString(val)); } public boolean forcePostBinding() { diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java b/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java index 3356c314738..9837179f049 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlConfigAttributes.java @@ -31,6 +31,7 @@ public interface SamlConfigAttributes { String SAML_AUTHNSTATEMENT = "saml.authnstatement"; String SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE = "saml_force_name_id_format"; String SAML_SERVER_SIGNATURE = "saml.server.signature"; + String SAML_SERVER_SIGNATURE_KEYINFO_EXT = "saml.server.signature.keyinfo.ext"; String SAML_FORCE_POST_BINDING = "saml.force.post.binding"; String SAML_ASSERTION_SIGNATURE = "saml.assertion.signature"; String SAML_ENCRYPT = "saml.encrypt"; diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java index 14726d35473..486633f46da 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocol.java @@ -74,8 +74,10 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; +import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; /** * @author Bill Burke @@ -98,6 +100,7 @@ public class SamlProtocol implements LoginProtocol { public static final String SAML_REDIRECT_BINDING = "get"; public static final String SAML_REQUEST_ID = "SAML_REQUEST_ID"; public static final String SAML_LOGOUT_BINDING = "saml.logout.binding"; + public static final String SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO = "saml.logout.addExtensionsElementWithKeyInfo"; public static final String SAML_LOGOUT_REQUEST_ID = "SAML_LOGOUT_REQUEST_ID"; public static final String SAML_LOGOUT_RELAY_STATE = "SAML_LOGOUT_RELAY_STATE"; public static final String SAML_LOGOUT_CANONICALIZATION = "SAML_LOGOUT_CANONICALIZATION"; @@ -373,7 +376,15 @@ public class SamlProtocol implements LoginProtocol { } Document samlDocument = null; + KeyManager keyManager = session.keys(); + KeyManager.ActiveKey keys = keyManager.getActiveKey(realm); + boolean postBinding = isPostBinding(clientSession); + try { + if ((! postBinding) && samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) { + builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid())); + } + ResponseType samlModel = builder.buildModel(); final AttributeStatementType attributeStatement = populateAttributeStatements(attributeStatementMappers, session, userSession, clientSession); populateRoles(roleListMapper, session, userSession, clientSession, attributeStatement); @@ -394,22 +405,19 @@ public class SamlProtocol implements LoginProtocol { JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder(); bindingBuilder.relayState(relayState); - KeyManager keyManager = session.keys(); - KeyManager.ActiveKey keys = keyManager.getActiveKey(realm); - if (samlClient.requiresRealmSignature()) { String canonicalization = samlClient.getCanonicalizationMethod(); if (canonicalization != null) { bindingBuilder.canonicalizationMethod(canonicalization); } - bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument(); + bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument(); } if (samlClient.requiresAssertionSignature()) { String canonicalization = samlClient.getCanonicalizationMethod(); if (canonicalization != null) { bindingBuilder.canonicalizationMethod(canonicalization); } - bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signAssertions(); + bindingBuilder.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signAssertions(); } if (samlClient.requiresEncryption()) { PublicKey publicKey = null; @@ -496,12 +504,17 @@ public class SamlProtocol implements LoginProtocol { if (isLogoutPostBindingForClient(clientSession)) { String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_POST_BINDING); SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client); + // This is POST binding, hence KeyID is included in dsig:KeyInfo/dsig:KeyName, no need to add element JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient); return binding.postBinding(logoutBuilder.buildDocument()).request(bindingUri); } else { logger.debug("frontchannel redirect binding"); String bindingUri = getLogoutServiceUrl(uriInfo, client, SAML_REDIRECT_BINDING); SAML2LogoutRequestBuilder logoutBuilder = createLogoutRequest(bindingUri, clientSession, client); + if (samlClient.requiresRealmSignature() && samlClient.addExtensionsElementWithKeyInfo()) { + KeyManager.ActiveKey keys = session.keys().getActiveKey(realm); + logoutBuilder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid())); + } JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient); return binding.redirectBinding(logoutBuilder.buildDocument()).request(bindingUri); } @@ -534,6 +547,7 @@ public class SamlProtocol implements LoginProtocol { JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(); binding.relayState(logoutRelayState); String signingAlgorithm = userSession.getNote(SAML_LOGOUT_SIGNATURE_ALGORITHM); + boolean postBinding = isLogoutPostBindingForInitiator(userSession); if (signingAlgorithm != null) { SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(signingAlgorithm); String canonicalization = userSession.getNote(SAML_LOGOUT_CANONICALIZATION); @@ -541,7 +555,11 @@ public class SamlProtocol implements LoginProtocol { binding.canonicalizationMethod(canonicalization); } KeyManager.ActiveKey keys = session.keys().getActiveKey(realm); - binding.signatureAlgorithm(algorithm).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument(); + binding.signatureAlgorithm(algorithm).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument(); + boolean addExtension = (! postBinding) && Objects.equals("true", userSession.getNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO)); + if (addExtension) { // Only include extension if REDIRECT binding and signing whole SAML protocol message + builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid())); + } } try { @@ -577,6 +595,7 @@ public class SamlProtocol implements LoginProtocol { String logoutRequestString = null; try { JaxrsSAML2BindingBuilder binding = createBindingBuilder(samlClient); + // This is POST binding, hence KeyID is included in dsig:KeyInfo/dsig:KeyName, no need to add element logoutRequestString = binding.postBinding(logoutBuilder.buildDocument()).encoded(); } catch (Exception e) { logger.warn("failed to send saml logout", e); @@ -639,7 +658,7 @@ public class SamlProtocol implements LoginProtocol { JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder(); if (samlClient.requiresRealmSignature()) { KeyManager.ActiveKey keys = session.keys().getActiveKey(realm); - binding.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument(); + binding.signatureAlgorithm(samlClient.getSignatureAlgorithm()).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument(); } return binding; } diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java index e1a7c98c0de..026a54a5993 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlProtocolUtils.java @@ -17,6 +17,7 @@ package org.keycloak.protocol.saml; +import java.security.Key; import org.keycloak.common.VerificationException; import org.keycloak.common.util.PemUtils; import org.keycloak.models.ClientModel; @@ -33,6 +34,15 @@ import javax.ws.rs.core.UriInfo; import java.security.PublicKey; import java.security.Signature; import java.security.cert.Certificate; +import org.keycloak.dom.saml.v2.SAML2Object; +import org.keycloak.dom.saml.v2.protocol.ExtensionsType; +import org.keycloak.dom.saml.v2.protocol.RequestAbstractType; +import org.keycloak.dom.saml.v2.protocol.StatusResponseType; +import org.keycloak.rotation.HardcodedKeyLocator; +import org.keycloak.rotation.KeyLocator; +import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; +import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; +import org.w3c.dom.Element; /** * @author Bill Burke @@ -40,20 +50,36 @@ import java.security.cert.Certificate; */ public class SamlProtocolUtils { - + /** + * Verifies a signature of the given SAML document using settings for the given client. + * Throws an exception if the client signature is expected to be present as per the client + * settings and it is invalid, otherwise returns back to the caller. + * + * @param client + * @param document + * @throws VerificationException + */ public static void verifyDocumentSignature(ClientModel client, Document document) throws VerificationException { SamlClient samlClient = new SamlClient(client); if (!samlClient.requiresClientSignature()) { return; } PublicKey publicKey = getSignatureValidationKey(client); - verifyDocumentSignature(document, publicKey); + verifyDocumentSignature(document, new HardcodedKeyLocator(publicKey)); } - public static void verifyDocumentSignature(Document document, PublicKey publicKey) throws VerificationException { + /** + * Verifies a signature of the given SAML document using keys obtained from the given key locator. + * Throws an exception if the client signature is invalid, otherwise returns back to the caller. + * + * @param document + * @param keyLocator + * @throws VerificationException + */ + public static void verifyDocumentSignature(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) { @@ -61,10 +87,22 @@ public class SamlProtocolUtils { } } + /** + * Returns public part of SAML signing key from the client settings. + * @param client + * @return Public key for signature validation. + * @throws VerificationException + */ public static PublicKey getSignatureValidationKey(ClientModel client) throws VerificationException { return getPublicKey(new SamlClient(client).getClientSigningCertificate()); } + /** + * Returns public part of SAML encryption key from the client settings. + * @param client + * @return Public key for encryption. + * @throws VerificationException + */ public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException { return getPublicKey(client, SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE); } @@ -85,7 +123,7 @@ public class SamlProtocolUtils { return cert.getPublicKey(); } - public static void verifyRedirectSignature(PublicKey publicKey, UriInfo uriInformation, String paramKey) throws VerificationException { + public static void verifyRedirectSignature(SAMLDocumentHolder documentHolder, KeyLocator locator, UriInfo uriInformation, String paramKey) throws VerificationException { MultivaluedMap encodedParams = uriInformation.getQueryParameters(false); String request = encodedParams.getFirst(paramKey); String algorithm = encodedParams.getFirst(GeneralConstants.SAML_SIG_ALG_REQUEST_KEY); @@ -96,10 +134,11 @@ public class SamlProtocolUtils { if (algorithm == null) throw new VerificationException("SigAlg was null"); if (signature == null) throw new VerificationException("Signature was null"); + String keyId = getMessageSigningKeyId(documentHolder.getSamlObject()); + // Shibboleth doesn't sign the document for redirect binding. // todo maybe a flag? - UriBuilder builder = UriBuilder.fromPath("/") .queryParam(paramKey, request); if (encodedParams.containsKey(GeneralConstants.RELAY_STATE)) { @@ -113,8 +152,13 @@ public class SamlProtocolUtils { SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.getFromXmlMethod(decodedAlgorithm); Signature validator = signatureAlgorithm.createSignature(); // todo plugin signature alg - validator.initVerify(publicKey); - validator.update(rawQuery.getBytes("UTF-8")); + Key key = locator.getKey(keyId); + if (key instanceof PublicKey) { + validator.initVerify((PublicKey) key); + validator.update(rawQuery.getBytes("UTF-8")); + } else { + throw new VerificationException("Invalid key locator for signature verification"); + } if (!validator.verify(decodedSignature)) { throw new VerificationException("Invalid query param signature"); } @@ -123,5 +167,32 @@ public class SamlProtocolUtils { } } + private static 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; + } } diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java b/services/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java index b2b4ee44406..a67374ad954 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlRepresentationAttributes.java @@ -64,7 +64,11 @@ public class SamlRepresentationAttributes { public String getSamlServerSignature() { if (getAttributes() == null) return null; return getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE); + } + public String getAddExtensionsElementWithKeyInfo() { + if (getAttributes() == null) return null; + return getAttributes().get(SamlConfigAttributes.SAML_SERVER_SIGNATURE_KEYINFO_EXT); } public String getForcePostBinding() { diff --git a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java index ea010856505..14c550396b1 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/SamlService.java +++ b/services/src/main/java/org/keycloak/protocol/saml/SamlService.java @@ -74,6 +74,17 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.security.PublicKey; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; +import java.util.TreeSet; +import org.keycloak.common.util.StringPropertyReplacer; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; +import org.keycloak.keys.KeyMetadata; +import org.keycloak.rotation.HardcodedKeyLocator; +import org.keycloak.rotation.KeyLocator; +import org.keycloak.saml.SPMetadataDescriptor; +import org.keycloak.saml.processing.core.util.KeycloakKeySamlExtensionGenerator; /** * Resource class for the oauth/openid connect token service @@ -336,6 +347,8 @@ public class SamlService extends AuthorizationEndpointBase { String logoutBinding = getBindingType(); if ("true".equals(samlClient.forcePostBinding())) logoutBinding = SamlProtocol.SAML_POST_BINDING; + boolean postBinding = Objects.equals(SamlProtocol.SAML_POST_BINDING, logoutBinding); + String bindingUri = SamlProtocol.getLogoutServiceUrl(uriInfo, client, logoutBinding); UserSessionModel userSession = authResult.getSession(); userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING_URI, bindingUri); @@ -347,6 +360,7 @@ public class SamlService extends AuthorizationEndpointBase { userSession.setNote(SamlProtocol.SAML_LOGOUT_RELAY_STATE, relayState); userSession.setNote(SamlProtocol.SAML_LOGOUT_REQUEST_ID, logoutRequest.getID()); userSession.setNote(SamlProtocol.SAML_LOGOUT_BINDING, logoutBinding); + userSession.setNote(SamlProtocol.SAML_LOGOUT_ADD_EXTENSIONS_ELEMENT_WITH_KEY_INFO, Boolean.toString((! postBinding) && samlClient.addExtensionsElementWithKeyInfo())); userSession.setNote(SamlProtocol.SAML_LOGOUT_CANONICALIZATION, samlClient.getCanonicalizationMethod()); userSession.setNote(AuthenticationManager.KEYCLOAK_LOGOUT_PROTOCOL, SamlProtocol.LOGIN_PROTOCOL); // remove client from logout requests @@ -397,14 +411,17 @@ public class SamlService extends AuthorizationEndpointBase { builder.destination(logoutBindingUri); builder.issuer(RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString()); JaxrsSAML2BindingBuilder binding = new JaxrsSAML2BindingBuilder().relayState(logoutRelayState); + boolean postBinding = SamlProtocol.SAML_POST_BINDING.equals(logoutBinding); if (samlClient.requiresRealmSignature()) { SignatureAlgorithm algorithm = samlClient.getSignatureAlgorithm(); KeyManager.ActiveKey keys = session.keys().getActiveKey(realm); - binding.signatureAlgorithm(algorithm).signWith(keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument(); - + binding.signatureAlgorithm(algorithm).signWith(keys.getKid(), keys.getPrivateKey(), keys.getPublicKey(), keys.getCertificate()).signDocument(); + if (! postBinding && samlClient.addExtensionsElementWithKeyInfo()) { // Only include extension if REDIRECT binding and signing whole SAML protocol message + builder.addExtension(new KeycloakKeySamlExtensionGenerator(keys.getKid())); + } } try { - if (SamlProtocol.SAML_POST_BINDING.equals(logoutBinding)) { + if (postBinding) { return binding.postBinding(builder.buildDocument()).response(logoutBindingUri); } else { return binding.redirectBinding(builder.buildDocument()).response(logoutBindingUri); @@ -466,7 +483,8 @@ public class SamlService extends AuthorizationEndpointBase { return; } PublicKey publicKey = SamlProtocolUtils.getSignatureValidationKey(client); - SamlProtocolUtils.verifyRedirectSignature(publicKey, uriInfo, GeneralConstants.SAML_REQUEST_KEY); + KeyLocator clientKeyLocator = new HardcodedKeyLocator(publicKey); + SamlProtocolUtils.verifyRedirectSignature(documentHolder, clientKeyLocator, uriInfo, GeneralConstants.SAML_REQUEST_KEY); } @Override @@ -541,12 +559,30 @@ public class SamlService extends AuthorizationEndpointBase { public static String getIDPMetadataDescriptor(UriInfo uriInfo, KeycloakSession session, RealmModel realm) throws IOException { InputStream is = SamlService.class.getResourceAsStream("/idp-metadata-template.xml"); String template = StreamUtil.readString(is); - template = template.replace("${idp.entityID}", RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString()); - template = template.replace("${idp.sso.HTTP-POST}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); - template = template.replace("${idp.sso.HTTP-Redirect}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); - template = template.replace("${idp.sls.HTTP-POST}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); - template = template.replace("${idp.signing.certificate}", PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate())); - return template; + Properties props = new Properties(); + props.put("idp.entityID", RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString()); + props.put("idp.sso.HTTP-POST", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); + props.put("idp.sso.HTTP-Redirect", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); + props.put("idp.sls.HTTP-POST", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString()); + StringBuilder keysString = new StringBuilder(); + Set keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list + ? (int) (o2.getProviderPriority() - o1.getProviderPriority()) + : (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1)); + keys.addAll(session.keys().getKeys(realm, false)); + for (KeyMetadata key : keys) { + addKeyInfo(keysString, key, KeyTypes.SIGNING.value()); + } + props.put("idp.signing.certificates", keysString.toString()); + return StringPropertyReplacer.replaceProperties(template, props); + } + + private static void addKeyInfo(StringBuilder target, KeyMetadata key, String purpose) { + if (key == null) { + return; + } + + target.append(SPMetadataDescriptor.xmlKeyInfo(" ", + key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, false)); } @GET diff --git a/services/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlClientInstallation.java b/services/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlClientInstallation.java index 2175b32cc6e..14166cef3e5 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlClientInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlClientInstallation.java @@ -18,7 +18,6 @@ package org.keycloak.protocol.saml.installation; import org.keycloak.Config; -import org.keycloak.common.util.PemUtils; import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; @@ -42,14 +41,14 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide @Override public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) { SamlClient samlClient = new SamlClient(client); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("\n"); baseXml(session, realm, client, baseUri, samlClient, buffer); buffer.append("\n"); return Response.ok(buffer.toString(), MediaType.TEXT_PLAIN_TYPE).build(); } - public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuffer buffer) { + public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuilder buffer) { buffer.append(" \n"); @@ -113,15 +112,6 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide buffer.append(" postBindingUrl=\"").append(bindingUrl).append("\"\n"); buffer.append(" redirectBindingUrl=\"").append(bindingUrl).append("\""); buffer.append("/>\n"); - if (samlClient.requiresRealmSignature()) { - buffer.append(" \n"); - buffer.append(" \n"); - buffer.append(" \n"); - buffer.append(" ").append(PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate())).append("\n"); - buffer.append(" \n"); - buffer.append(" \n"); - buffer.append(" \n"); - } buffer.append(" \n"); buffer.append(" \n"); } @@ -138,7 +128,7 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide @Override public String getHelpText() { - return "Keycloak SAML adapter configuration file. Put this in WEB-INF directory if your WAR."; + return "Keycloak SAML adapter configuration file. Put this in WEB-INF directory of your WAR."; } @Override diff --git a/services/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlSubsystemInstallation.java b/services/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlSubsystemInstallation.java index ea77d474b55..bde0ccd3d98 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlSubsystemInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/saml/installation/KeycloakSamlSubsystemInstallation.java @@ -39,7 +39,7 @@ public class KeycloakSamlSubsystemInstallation implements ClientInstallationProv @Override public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) { SamlClient samlClient = new SamlClient(client); - StringBuffer buffer = new StringBuffer(); + StringBuilder buffer = new StringBuilder(); buffer.append("\n"); KeycloakSamlClientInstallation.baseXml(session, realm, client, baseUri, samlClient, buffer); buffer.append("\n"); diff --git a/services/src/main/java/org/keycloak/protocol/saml/installation/SamlIDPDescriptorClientInstallation.java b/services/src/main/java/org/keycloak/protocol/saml/installation/SamlIDPDescriptorClientInstallation.java index 4b84363809c..3c451b38570 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/installation/SamlIDPDescriptorClientInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/saml/installation/SamlIDPDescriptorClientInstallation.java @@ -32,6 +32,11 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import java.net.URI; +import java.util.Set; +import java.util.TreeSet; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; +import org.keycloak.keys.KeyMetadata; +import org.keycloak.saml.SPMetadataDescriptor; /** * @author Bill Burke @@ -41,49 +46,61 @@ public class SamlIDPDescriptorClientInstallation implements ClientInstallationPr public static String getIDPDescriptorForClient(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri) { SamlClient samlClient = new SamlClient(client); String idpEntityId = RealmsResource.realmBaseUrl(UriBuilder.fromUri(serverBaseUri)).build(realm.getName()).toString(); - String idp = "\n" + - "\n" + - " \n"; + StringBuilder sb = new StringBuilder(); + sb.append("\n" + + "\n" + + " \n"); if (samlClient.forceNameIDFormat() && samlClient.getNameIDFormat() != null) { - idp += " " + samlClient.getNameIDFormat() + "\n"; + sb.append(" ").append(samlClient.getNameIDFormat()).append("\n"); } else { - idp += " urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\n" + - " urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n" + - " urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\n" + - " urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\n"; + sb.append(" urn:oasis:names:tc:SAML:2.0:nameid-format:persistent\n" + + " urn:oasis:names:tc:SAML:2.0:nameid-format:transient\n" + + " urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified\n" + + " urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress\n"); } String bindUrl = RealmsResource.protocolUrl(UriBuilder.fromUri(serverBaseUri)).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString(); - idp += "\n" + - " \n"; - if (!samlClient.forcePostBinding()) { - idp += " \n"; + sb.append("\n" + + " \n"); + if (! samlClient.forcePostBinding()) { + sb.append(" \n"); } - idp += " \n"; - if (!samlClient.forcePostBinding()) { - idp += " \n"; + sb.append(" \n"); + if (! samlClient.forcePostBinding()) { + sb.append(" \n"); } - idp += " \n" + - " \n" + - " \n" + - " \n" + - " " + PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate()) + "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - "\n"; - return idp; + + Set keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list + ? (int) (o2.getProviderPriority() - o1.getProviderPriority()) + : (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1)); + keys.addAll(session.keys().getKeys(realm, false)); + for (KeyMetadata key : keys) { + addKeyInfo(sb, key, KeyTypes.SIGNING.value()); + } + + sb.append(" \n" + + "\n"); + return sb.toString(); + } + + private static void addKeyInfo(StringBuilder target, KeyMetadata key, String purpose) { + if (key == null) { + return; + } + + target.append(SPMetadataDescriptor.xmlKeyInfo(" ", key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, false)); } @Override diff --git a/services/src/main/java/org/keycloak/protocol/saml/installation/SamlSPDescriptorClientInstallation.java b/services/src/main/java/org/keycloak/protocol/saml/installation/SamlSPDescriptorClientInstallation.java index 9d1224259f3..63499534373 100755 --- a/services/src/main/java/org/keycloak/protocol/saml/installation/SamlSPDescriptorClientInstallation.java +++ b/services/src/main/java/org/keycloak/protocol/saml/installation/SamlSPDescriptorClientInstallation.java @@ -31,6 +31,7 @@ import org.keycloak.saml.common.constants.JBossSAMLURIConstants; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.net.URI; +import org.keycloak.dom.saml.v2.metadata.KeyTypes; /** * @author Bill Burke @@ -45,7 +46,8 @@ public class SamlSPDescriptorClientInstallation implements ClientInstallationPro if (logoutUrl == null) logoutUrl = client.getManagementUrl(); String nameIdFormat = samlClient.getNameIDFormat(); if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT; - return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl, samlClient.requiresClientSignature(), client.getClientId(), nameIdFormat, samlClient.getClientSigningCertificate()); + String spCertificate = SPMetadataDescriptor.xmlKeyInfo(" ", null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true); + return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl, samlClient.requiresClientSignature(), client.getClientId(), nameIdFormat, spCertificate); } @Override diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java index 99f2559ef47..07313d87ba0 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakContext.java @@ -110,13 +110,6 @@ public class DefaultKeycloakContext implements KeycloakContext { this.connection = connection; } - @Override - public RealmImporter getRealmManager() { - RealmManager manager = new RealmManager(session); - manager.setContextPath(getContextPath()); - return manager; - } - @Override public Locale resolveLocale(UserModel user) { return LocaleHelper.getLocale(session, realm, user); diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java index 7fbd7a3d12b..4cc77a269ea 100644 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSession.java @@ -59,7 +59,6 @@ public class DefaultKeycloakSession implements KeycloakSession { private UserProvider userModel; private UserStorageManager userStorageManager; private UserCredentialStoreManager userCredentialStorageManager; - private ScriptingProvider scriptingProvider; private UserSessionProvider sessionProvider; private UserFederationManager federationManager; private UserFederatedStorageProvider userFederatedStorageProvider; @@ -275,14 +274,4 @@ public class DefaultKeycloakSession implements KeycloakSession { } } } - - @Override - public ScriptingProvider scripting() { - - if (scriptingProvider == null) { - scriptingProvider = getProvider(ScriptingProvider.class); - } - - return scriptingProvider; - } } diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index d0eb66b4956..b3f2638e996 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -436,7 +436,11 @@ public class AuthenticationManager { // refresh the cookies! createLoginCookie(session, realm, userSession.getUser(), userSession, uriInfo, clientConnection); if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN); - if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getLoginUsername(), uriInfo, clientConnection); + if (userSession.isRememberMe()) { + createRememberMeCookie(realm, userSession.getLoginUsername(), uriInfo, clientConnection); + } else { + expireRememberMeCookie(realm, uriInfo, clientConnection); + } // Update userSession note with authTime. But just if flag SSO_AUTH is not set if (!isSSOAuthentication(clientSession)) { diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java index b648bbd6b80..eb6fba623cd 100644 --- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java +++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java @@ -27,6 +27,7 @@ import org.keycloak.models.ClientModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.RealmModel; +import org.keycloak.models.UserManager; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionProvider; import org.keycloak.models.session.UserSessionPersisterProvider; diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java index f9ae1907676..7a8964f6da0 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java @@ -66,7 +66,7 @@ import org.keycloak.services.Urls; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.BruteForceProtector; import org.keycloak.services.managers.ClientSessionCode; -import org.keycloak.services.managers.UserManager; +import org.keycloak.models.UserManager; import org.keycloak.services.managers.UserSessionManager; import org.keycloak.services.resources.AccountService; import org.keycloak.services.validation.Validation; diff --git a/services/src/main/resources/idp-metadata-template.xml b/services/src/main/resources/idp-metadata-template.xml index 0a536478f68..a4416cdaa98 100755 --- a/services/src/main/resources/idp-metadata-template.xml +++ b/services/src/main/resources/idp-metadata-template.xml @@ -16,22 +16,12 @@ ~ limitations under the License. --> - - - - - - - ${idp.signing.certificate} - - - - +${idp.signing.certificates} diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml index 28e478911c4..6f208386fdc 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/org/keycloak/testsuite/integration-arquillian-testsuite-providers/main/module.xml @@ -28,6 +28,7 @@ + diff --git a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerDatabaseServlet.java b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerDatabaseServlet.java index 7392dd0950e..d6d038a3180 100644 --- a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerDatabaseServlet.java +++ b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerDatabaseServlet.java @@ -40,7 +40,13 @@ public class CustomerDatabaseServlet extends HttpServlet { pw.println("Bill Burke"); pw.print(""); pw.flush(); - - + } + + @Override + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.logout(); + PrintWriter pw = resp.getWriter(); + pw.println("servlet logout from database ok"); + pw.flush(); } } diff --git a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServlet.java b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServlet.java index e174d7e0073..b4fd9a50f96 100644 --- a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServlet.java +++ b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServlet.java @@ -43,16 +43,38 @@ public class CustomerServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { PrintWriter pw = resp.getWriter(); + KeycloakSecurityContext context = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName()); if (req.getRequestURI().endsWith("logout")) { resp.setStatus(200); pw.println("servlet logout ok"); + + //Clear principal form database-service by calling logout + StringBuilder result = new StringBuilder(); + String urlBase; + if (System.getProperty("app.server.ssl.required", "false").equals("true")) { + urlBase = System.getProperty("app.server.ssl.base.url", "https://localhost:8643"); + } else { + urlBase = System.getProperty("app.server.base.url", "http://localhost:8280"); + } + + URL url = new URL(urlBase + "/customer-db/"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("DELETE"); + conn.setRequestProperty(HttpHeaders.AUTHORIZATION, "Bearer " + context.getTokenString()); + BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = rd.readLine()) != null) { + result.append(line); + } + rd.close(); + pw.println(result.toString()); // Call logout before pw.flush req.logout(); pw.flush(); return; } - KeycloakSecurityContext context = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName()); + //try { StringBuilder result = new StringBuilder(); diff --git a/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServletNoConf.java b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServletNoConf.java new file mode 100644 index 00000000000..c353532dc55 --- /dev/null +++ b/testsuite/integration-arquillian/test-apps/servlets/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServletNoConf.java @@ -0,0 +1,95 @@ +/* + * 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.testsuite.adapter.servlet; + +import org.keycloak.KeycloakSecurityContext; + +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.ws.rs.core.HttpHeaders; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.HttpURLConnection; +import java.net.URL; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@WebServlet("/customer-portal-noconf") +public class CustomerServletNoConf extends HttpServlet { + private static final String LINK = "%s"; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + PrintWriter pw = resp.getWriter(); + if (req.getRequestURI().endsWith("logout")) { + resp.setStatus(200); + pw.println("servlet logout ok"); + + // Call logout before pw.flush + req.logout(); + pw.flush(); + return; + } + KeycloakSecurityContext context = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName()); + + //try { + StringBuilder result = new StringBuilder(); + String urlBase; + + if (System.getProperty("app.server.ssl.required", "false").equals("true")) { + urlBase = System.getProperty("app.server.ssl.base.url", "https://localhost:8643"); + } else { + urlBase = System.getProperty("app.server.base.url", "http://localhost:8280"); + } + + URL url = new URL(urlBase + "/customer-db/"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + conn.setRequestProperty(HttpHeaders.AUTHORIZATION, "Bearer " + context.getTokenString()); + BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream())); + String line; + while ((line = rd.readLine()) != null) { + result.append(line); + } + rd.close(); + resp.setContentType("text/html"); + pw.println(result.toString()); + pw.flush(); +// +// Response response = target.request().get(); +// if (response.getStatus() != 401) { // assert response status == 401 +// throw new AssertionError("Response status code is not 401."); +// } +// response.close(); +// String html = target.request() +// .header(HttpHeaders.AUTHORIZATION, "Bearer " + context.getTokenString()) +// .get(String.class); +// pw.println(html); +// pw.flush(); +// } finally { +// client.close(); +// } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortalNoConf.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortalNoConf.java new file mode 100644 index 00000000000..ab17aa7685d --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortalNoConf.java @@ -0,0 +1,39 @@ +/* + * 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.testsuite.adapter.page; + +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl; + +import java.net.URL; + +public class CustomerPortalNoConf extends AbstractPageWithInjectedUrl { + + public static final String DEPLOYMENT_NAME = "customer-portal-noconf"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + return url; + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigPostNoIdpKeyServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigPostNoIdpKeyServlet.java new file mode 100644 index 00000000000..5ef40ae77df --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigPostNoIdpKeyServlet.java @@ -0,0 +1,39 @@ +/* + * 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.testsuite.adapter.page; + +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; + +import java.net.URL; + +/** + * @author hmlnarik + */ +public class EmployeeSigPostNoIdpKeyServlet extends SAMLServlet { + public static final String DEPLOYMENT_NAME = "employee-sig-post-noidpkey"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + return url; + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigRedirNoIdpKeyServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigRedirNoIdpKeyServlet.java new file mode 100644 index 00000000000..ac6d67184d3 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigRedirNoIdpKeyServlet.java @@ -0,0 +1,39 @@ +/* + * 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.testsuite.adapter.page; + +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; + +import java.net.URL; + +/** + * @author hmlnarik + */ +public class EmployeeSigRedirNoIdpKeyServlet extends SAMLServlet { + public static final String DEPLOYMENT_NAME = "employee-sig-redir-noidpkey"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + return url; + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigRedirOptNoIdpKeyServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigRedirOptNoIdpKeyServlet.java new file mode 100644 index 00000000000..e37c12f6902 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/EmployeeSigRedirOptNoIdpKeyServlet.java @@ -0,0 +1,39 @@ +/* + * 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.testsuite.adapter.page; + +import org.jboss.arquillian.container.test.api.OperateOnDeployment; +import org.jboss.arquillian.test.api.ArquillianResource; + +import java.net.URL; + +/** + * @author hmlnarik + */ +public class EmployeeSigRedirOptNoIdpKeyServlet extends SAMLServlet { + public static final String DEPLOYMENT_NAME = "employee-sig-redir-opt-noidpkey"; + + @ArquillianResource + @OperateOnDeployment(DEPLOYMENT_NAME) + private URL url; + + @Override + public URL getInjectedUrl() { + return url; + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java index 374585b3f22..b8ed52d582e 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java @@ -132,21 +132,12 @@ public class OAuthClient { public AuthorizationEndpointResponse doLogin(String username, String password) { openLoginForm(); - String src = driver.getPageSource(); - try { - driver.findElement(By.id("username")).sendKeys(username); - driver.findElement(By.id("password")).sendKeys(password); - driver.findElement(By.name("login")).click(); - } catch (Throwable t) { - System.err.println(src); - throw t; - } + fillLoginForm(username, password); return new AuthorizationEndpointResponse(this); } - public void doLoginGrant(String username, String password) { - openLoginForm(); + public void fillLoginForm(String username, String password) { String src = driver.getPageSource(); try { driver.findElement(By.id("username")).sendKeys(username); @@ -158,6 +149,11 @@ public class OAuthClient { } } + public void doLoginGrant(String username, String password) { + openLoginForm(); + fillLoginForm(username, password); + } + public AccessTokenResponse doAccessTokenRequest(String code, String password) { CloseableHttpClient client = new DefaultHttpClient(); try { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 0dc7597594b..65a2e9bf6d3 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -813,4 +813,18 @@ public class AccountTest extends TestRealmKeycloakTest { } + @Test + public void testInvalidReferrer() { + driver.navigate().to(profilePage.getPath() + "?referrer=test-app"); + loginPage.login("test-user@localhost", "password"); + Assert.assertTrue(profilePage.isCurrent()); + profilePage.backToApplication(); + + Assert.assertTrue(appPage.isCurrent()); + + driver.navigate().to(profilePage.getPath() + "?referrer=test-invalid&referrer_uri=http://localhost:8180/auth/realms/master/app/auth?test"); + Assert.assertTrue(profilePage.isCurrent()); + + events.clear(); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java index e742b91cb31..ed2191d2512 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java @@ -28,8 +28,10 @@ import org.junit.Test; import org.keycloak.OAuth2Constants; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.common.Version; +import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.Time; import org.keycloak.constants.AdapterConstants; +import org.keycloak.keys.KeyProvider; import org.keycloak.models.Constants; import org.keycloak.protocol.oidc.OIDCAdvancedConfigWrapper; import org.keycloak.protocol.oidc.OIDCLoginProtocol; @@ -37,6 +39,7 @@ import org.keycloak.protocol.oidc.OIDCLoginProtocolService; import org.keycloak.representations.AccessToken; import org.keycloak.representations.VersionRepresentation; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest; @@ -85,6 +88,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import org.keycloak.testsuite.adapter.page.CustomerPortalNoConf; import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals; import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith; @@ -101,6 +105,8 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd @Page private CustomerPortal customerPortal; @Page + private CustomerPortalNoConf customerPortalNoConf; + @Page private CustomerPortalSubsystem customerPortalSubsystem; @Page private SecurePortal securePortal; @@ -129,6 +135,11 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd protected static WebArchive customerPortal() { return servletDeployment(CustomerPortal.DEPLOYMENT_NAME, CustomerServlet.class, ErrorServlet.class); } + + @Deployment(name = CustomerPortalNoConf.DEPLOYMENT_NAME) + protected static WebArchive customerPortalNoConf() { + return servletDeployment(CustomerPortalNoConf.DEPLOYMENT_NAME, CustomerServletNoConf.class, ErrorServlet.class); + } @Deployment(name = CustomerPortalSubsystem.DEPLOYMENT_NAME) protected static WebArchive customerPortalSubsystem() { @@ -245,25 +256,26 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd driver.navigate().to(logoutUri); // Generate new realm key - RealmRepresentation realmRep = testRealmResource().toRepresentation(); - String oldPublicKey = realmRep.getPublicKey(); - String oldPrivateKey = realmRep.getPrivateKey(); - realmRep.setPublicKey(Constants.GENERATE); - testRealmResource().update(realmRep); - - // Try to login again. It should fail now - tokenMinTTLPage.navigateTo(); - testRealmLoginPage.form().waitForUsernameInputPresent(); - assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); - testRealmLoginPage.form().login("bburke@redhat.com", "password"); - URLAssert.assertCurrentUrlStartsWith(driver, tokenMinTTLPage.getInjectedUrl().toString()); - assertNull(tokenMinTTLPage.getAccessToken()); + String realmId = adminClient.realm(DEMO).toRepresentation().getId(); + ComponentRepresentation keys = new ComponentRepresentation(); + keys.setName("generated"); + keys.setProviderType(KeyProvider.class.getName()); + keys.setProviderId("rsa-generated"); + keys.setParentId(realmId); + keys.setConfig(new MultivaluedHashMap<>()); + keys.getConfig().putSingle("priority", "100"); + Response response = adminClient.realm(DEMO).components().add(keys); + assertEquals(201, response.getStatus()); + response.close(); String adapterActionsUrl = tokenMinTTLPage.toString() + "/unsecured/foo"; setAdapterAndServerTimeOffset(300, adapterActionsUrl); // Try to login. Should work now due to realm key change tokenMinTTLPage.navigateTo(); + testRealmLoginPage.form().waitForUsernameInputPresent(); + assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); + testRealmLoginPage.form().login("bburke@redhat.com", "password"); assertCurrentUrlEquals(tokenMinTTLPage); token = tokenMinTTLPage.getAccessToken(); Assert.assertEquals("bburke@redhat.com", token.getPreferredUsername()); @@ -387,7 +399,9 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd // test logout driver.navigate().to(customerPortal + "/logout"); - assertTrue(driver.getPageSource().contains("servlet logout ok")); + pageSource = driver.getPageSource(); + assertTrue(pageSource.contains("servlet logout ok")); + assertTrue(pageSource.contains("servlet logout from database ok")); customerPortal.navigateTo(); assertCurrentUrlStartsWithLoginUrlOf(testRealmPage); @@ -829,6 +843,13 @@ public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAd log.info("Checking app server log on app-server: \"" + System.getProperty("app.server") + "\" is not supported."); } } + + @Test + public void testWithoutKeycloakConf() { + customerPortalNoConf.navigateTo(); + String pageSource = driver.getPageSource(); + assertTrue(pageSource.contains("Forbidden") || pageSource.contains("HTTP Status 401")); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java index 290677848d7..6dd3ee36aab 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSAMLServletsAdapterTest.java @@ -24,6 +24,12 @@ import org.junit.Assert; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ProtocolMappersResource; +import org.keycloak.common.util.KeyUtils; +import org.keycloak.common.util.PemUtils; +import org.keycloak.keys.Attributes; +import org.keycloak.keys.KeyProvider; +import org.keycloak.keys.RsaKeyProviderFactory; +import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.protocol.saml.mappers.AttributeStatementHelper; import org.keycloak.protocol.saml.mappers.RoleListMapper; import org.keycloak.representations.idm.ClientRepresentation; @@ -40,6 +46,9 @@ import org.keycloak.testsuite.adapter.page.BadRealmSalesPostSigServlet; import org.keycloak.testsuite.adapter.page.Employee2Servlet; import org.keycloak.testsuite.adapter.page.EmployeeServlet; import org.keycloak.testsuite.adapter.page.EmployeeSigFrontServlet; +import org.keycloak.testsuite.adapter.page.EmployeeSigPostNoIdpKeyServlet; +import org.keycloak.testsuite.adapter.page.EmployeeSigRedirNoIdpKeyServlet; +import org.keycloak.testsuite.adapter.page.EmployeeSigRedirOptNoIdpKeyServlet; import org.keycloak.testsuite.adapter.page.EmployeeSigServlet; import org.keycloak.testsuite.adapter.page.InputPortal; import org.keycloak.testsuite.adapter.page.MissingAssertionSig; @@ -80,6 +89,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.net.URI; import java.net.URL; +import java.security.KeyPair; +import java.security.PublicKey; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -109,6 +120,15 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd @Page protected EmployeeSigServlet employeeSigServletPage; + @Page + protected EmployeeSigPostNoIdpKeyServlet employeeSigPostNoIdpKeyServletPage; + + @Page + protected EmployeeSigRedirNoIdpKeyServlet employeeSigRedirNoIdpKeyServletPage; + + @Page + protected EmployeeSigRedirOptNoIdpKeyServlet employeeSigRedirOptNoIdpKeyServletPage; + @Page protected EmployeeSigFrontServlet employeeSigFrontServletPage; @@ -184,6 +204,21 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd return samlServletDeployment(EmployeeSigServlet.DEPLOYMENT_NAME, SendUsernameServlet.class); } + @Deployment(name = EmployeeSigPostNoIdpKeyServlet.DEPLOYMENT_NAME) + protected static WebArchive employeeSigPostNoIdpKeyServlet() { + return samlServletDeployment(EmployeeSigPostNoIdpKeyServlet.DEPLOYMENT_NAME, SendUsernameServlet.class); + } + + @Deployment(name = EmployeeSigRedirNoIdpKeyServlet.DEPLOYMENT_NAME) + protected static WebArchive employeeSigRedirNoIdpKeyServlet() { + return samlServletDeployment(EmployeeSigRedirNoIdpKeyServlet.DEPLOYMENT_NAME, SendUsernameServlet.class); + } + + @Deployment(name = EmployeeSigRedirOptNoIdpKeyServlet.DEPLOYMENT_NAME) + protected static WebArchive employeeSigRedirOptNoIdpKeyServlet() { + return samlServletDeployment(EmployeeSigRedirOptNoIdpKeyServlet.DEPLOYMENT_NAME, SendUsernameServlet.class); + } + @Deployment(name = EmployeeSigFrontServlet.DEPLOYMENT_NAME) protected static WebArchive employeeSigFront() { return samlServletDeployment(EmployeeSigFrontServlet.DEPLOYMENT_NAME, SendUsernameServlet.class); @@ -394,6 +429,69 @@ public abstract class AbstractSAMLServletsAdapterTest extends AbstractServletsAd testSuccessfulAndUnauthorizedLogin(employeeSigServletPage, testRealmSAMLRedirectLoginPage); } + private PublicKey createKeys(String priority) throws Exception { + KeyPair keyPair = KeyUtils.generateRsaKeyPair(1024); + String privateKeyPem = PemUtils.encodeKey(keyPair.getPrivate()); + PublicKey publicKey = keyPair.getPublic(); + + ComponentRepresentation rep = new ComponentRepresentation(); + rep.setName("mycomponent"); + rep.setParentId("demo"); + rep.setProviderId(RsaKeyProviderFactory.ID); + rep.setProviderType(KeyProvider.class.getName()); + + org.keycloak.common.util.MultivaluedHashMap config = new org.keycloak.common.util.MultivaluedHashMap(); + config.addFirst("priority", priority); + config.addFirst(Attributes.PRIVATE_KEY_KEY, privateKeyPem); + rep.setConfig(config); + + testRealmResource().components().add(rep); + + return publicKey; + } + + private void dropKeys(String priority) { + for (ComponentRepresentation c : testRealmResource().components().query("demo", KeyProvider.class.getName())) { + if (c.getConfig().getFirst("priority").equals(priority)) { + testRealmResource().components().component(c.getId()).remove(); + return; + } + } + throw new RuntimeException("Failed to find keys"); + } + + private void testRotatedKeysPropagated(SAMLServlet servletPage, Login loginPage) throws Exception { + boolean keyDropped = false; + try { + log.info("Creating new key"); + createKeys("1000"); + testSuccessfulAndUnauthorizedLogin(servletPage, loginPage); + log.info("Dropping new key"); + dropKeys("1000"); + keyDropped = true; + testSuccessfulAndUnauthorizedLogin(servletPage, loginPage); + } finally { + if (! keyDropped) { + dropKeys("1000"); + } + } + } + + @Test + public void employeeSigPostNoIdpKeyTest() throws Exception { + testRotatedKeysPropagated(employeeSigPostNoIdpKeyServletPage, testRealmSAMLPostLoginPage); + } + + @Test + public void employeeSigRedirNoIdpKeyTest() throws Exception { + testRotatedKeysPropagated(employeeSigRedirNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage); + } + + @Test + public void employeeSigRedirOptNoIdpKeyTest() throws Exception { + testRotatedKeysPropagated(employeeSigRedirOptNoIdpKeyServletPage, testRealmSAMLRedirectLoginPage); + } + @Test public void employeeSigFrontTest() { testSuccessfulAndUnauthorizedLogin(employeeSigFrontServletPage, testRealmSAMLRedirectLoginPage); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java index 50a50272483..97edc00361e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/ApiUtil.java @@ -83,7 +83,7 @@ public class ApiUtil { public static ClientResource findClientResourceByName(RealmResource realm, String name) { for (ClientRepresentation c : realm.clients().findAll()) { - if (c.getName().equals(name)) { + if (name.equals(c.getName())) { return realm.clients().get(c.getId()); } } @@ -92,7 +92,7 @@ public class ApiUtil { public static ClientResource findClientByClientId(RealmResource realm, String clientId) { for (ClientRepresentation c : realm.clients().findAll()) { - if (c.getClientId().equals(clientId)) { + if (clientId.equals(c.getClientId())) { return realm.clients().get(c.getId()); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java index e3392b3a6e0..f550c17e437 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/IdentityProviderTest.java @@ -57,17 +57,43 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import javax.xml.crypto.dsig.XMLSignature; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.hamcrest.Matchers.*; +import org.keycloak.dom.saml.v2.metadata.KeyDescriptorType; +import org.w3c.dom.NodeList; /** * @author Stian Thorgersen */ public class IdentityProviderTest extends AbstractAdminTest { + // Certificate imported from + private static final String SIGNING_CERT_1 = "MIICmzCCAYMCBgFUYnC0OjANBgkqhkiG9w0BAQsFADARMQ8wDQY" + + "DVQQDDAZtYXN0ZXIwHhcNMTYwNDI5MTQzMjEzWhcNMjYwNDI5MTQzMzUzWjARMQ8wDQYDVQQDDAZtYXN0ZXI" + + "wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCN25AW1poMEZRbuMAHG58AThZmCwMV6/Gcui4mjGa" + + "cRFyudgqzLjQ2rxpoW41JAtLjbjeAhuWvirUcFVcOeS3gM/ZC27qCpYighAcylZz6MYocnEe1+e8rPPk4JlI" + + "D6Wv62dgu+pL/vYsQpRhvD3Y2c/ytgr5D32xF+KnzDehUy5BSyzypvu12Wq9mS5vK5tzkN37EjkhpY2ZxaXP" + + "ubjDIITCAL4Q8M/m5IlacBaUZbzI4AQrHnMP1O1IH2dHSWuMiBe+xSDTco72PmuYPJKTV4wQdeBUIkYbfLc4" + + "RxVmXEvgkQgyW86EoMPxlWJpj7+mTIR+l+2thZPr/VgwTs82rAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA/" + + "Ip/Hi8RoVu5ouaFFlc5whT7ltuK8slfLGW4tM4vJXhInYwsqIRQKBNDYW/64xle3eII4u1yAH1OYRRwEs7Em" + + "1pr4QuFuTY1at+aE0sE46XDlyESI0txJjWxYoT133vM0We2pj1b2nxgU30rwjKA3whnKEfTEYT/n3JBSqNgg" + + "y6l8ZGw/oPSgvPaR4+xeB1tfQFC4VrLoYKoqH6hAL530nKxL+qV8AIfL64NDEE8ankIAEDAAFe8x3CPUfXR/" + + "p4KOANKkpz8ieQaHDb1eITkAwUwjESj6UF9D1aePlhWls/HX0gujFXtWfWfrJ8CU/ogwlH8y1jgRuLjFQYZk6llc="; + + private static final String SIGNING_CERT_2 = "MIIBnDCCAQUCBgFYKXKsPTANBgkqhkiG9w0BAQsFADAUMRIwEAY" + + "DVQQDDAlzYW1sLWRlbW8wHhcNMTYxMTAzMDkwNzEwWhcNMjYxMTAzMDkwODUwWjAUMRIwEAYDVQQDDAlzYW1" + + "sLWRlbW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKtWsK5O0CtuBpnMvWG+HTG0vmZzujQ2o9WdheQ" + + "u+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfdQ2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQ" + + "CAqeyLv00yj6foqdJjxh5SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAhet" + + "vOU8TyqfZF5jpv0IcrviLl/DoFrbjByeHR+pu/vClcAOjL/u7oQELuuTfNsBI4tpexUj5G8q/YbEz0gk7idf" + + "LXrAUVcsR73oTngrhRfwUSmPrjjK0kjcRb6HL9V/+wh3R/6mEd59U08ExT8N38rhmn0CI3ehMdebReprP7U8="; + @Test public void testFindAll() { create(createRep("google", "google")); @@ -303,7 +329,45 @@ public class IdentityProviderTest extends AbstractAdminTest { form.addFormData("file", body, MediaType.APPLICATION_XML_TYPE, "saml-idp-metadata.xml"); Map result = realm.identityProviders().importFrom(form); - assertSamlImport(result); + assertSamlImport(result, SIGNING_CERT_1); + + // Create new SAML identity provider using configuration retrieved from import-config + create(createRep("saml", "saml", result)); + + IdentityProviderResource provider = realm.identityProviders().get("saml"); + IdentityProviderRepresentation rep = provider.toRepresentation(); + assertCreatedSamlIdp(rep); + + // Now list the providers - we should see the one just created + List providers = realm.identityProviders().findAll(); + Assert.assertNotNull("identityProviders not null", providers); + Assert.assertEquals("identityProviders instance count", 1, providers.size()); + assertEqual(rep, providers.get(0)); + + // Perform export, and make sure some of the values are like they're supposed to be + Response response = realm.identityProviders().get("saml").export("xml"); + Assert.assertEquals(200, response.getStatus()); + body = response.readEntity(String.class); + response.close(); + + assertSamlExport(body); + } + + @Test + public void testSamlImportAndExportMultipleSigningKeys() throws URISyntaxException, IOException, ParsingException { + + // Use import-config to convert IDPSSODescriptor file into key value pairs + // to use when creating a SAML Identity Provider + MultipartFormDataOutput form = new MultipartFormDataOutput(); + form.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE); + + URL idpMeta = getClass().getClassLoader().getResource("admin-test/saml-idp-metadata-two-signing-certs.xml"); + byte [] content = Files.readAllBytes(Paths.get(idpMeta.toURI())); + String body = new String(content, Charset.forName("utf-8")); + form.addFormData("file", body, MediaType.APPLICATION_XML_TYPE, "saml-idp-metadata-two-signing-certs"); + + Map result = realm.identityProviders().importFrom(form); + assertSamlImport(result, SIGNING_CERT_1 + "," + SIGNING_CERT_2); // Create new SAML identity provider using configuration retrieved from import-config create(createRep("saml", "saml", result)); @@ -464,18 +528,29 @@ public class IdentityProviderTest extends AbstractAdminTest { // import endpoint simply converts IDPSSODescriptor into key value pairs. // check that saml-idp-metadata.xml was properly converted into key value pairs //System.out.println(config); - Assert.assertEquals("Config size", 7, config.size()); - Assert.assertEquals("validateSignature", "true", config.get("validateSignature")); - Assert.assertEquals("singleLogoutServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml", config.get("singleLogoutServiceUrl")); - Assert.assertEquals("postBindingResponse", "true", config.get("postBindingResponse")); - Assert.assertEquals("postBindingAuthnRequest", "true", config.get("postBindingAuthnRequest")); - Assert.assertEquals("singleSignOnServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml", config.get("singleSignOnServiceUrl")); - Assert.assertEquals("wantAuthnRequestsSigned", "true", config.get("wantAuthnRequestsSigned")); - Assert.assertNotNull("signingCertificate not null", config.get("signingCertificate")); + assertThat(config.keySet(), containsInAnyOrder( + "validateSignature", + "singleLogoutServiceUrl", + "postBindingResponse", + "postBindingAuthnRequest", + "singleSignOnServiceUrl", + "wantAuthnRequestsSigned", + "signingCertificate", + "addExtensionsElementWithKeyInfo" + )); + assertThat(config, hasEntry("validateSignature", "true")); + assertThat(config, hasEntry("singleLogoutServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml")); + assertThat(config, hasEntry("postBindingResponse", "true")); + assertThat(config, hasEntry("postBindingAuthnRequest", "true")); + assertThat(config, hasEntry("singleSignOnServiceUrl", "http://localhost:8080/auth/realms/master/protocol/saml")); + assertThat(config, hasEntry("wantAuthnRequestsSigned", "true")); + assertThat(config, hasEntry("addExtensionsElementWithKeyInfo", "false")); + assertThat(config, hasEntry(is("signingCertificate"), notNullValue())); } - private void assertSamlImport(Map config) { + private void assertSamlImport(Map config, String expectedSigningCertificates) { assertSamlConfig(config); + assertThat(config, hasEntry("signingCertificate", expectedSigningCertificates)); } private void assertSamlExport(String body) throws ParsingException, URISyntaxException { @@ -534,7 +609,11 @@ public class IdentityProviderTest extends AbstractAdminTest { Assert.assertNotNull("KeyDescriptor not null", desc.getKeyDescriptor()); Assert.assertEquals("KeyDescriptor.size", 1, desc.getKeyDescriptor().size()); - Assert.assertEquals("KeyDescriptor.Use", KeyTypes.SIGNING, desc.getKeyDescriptor().get(0).getUse()); + KeyDescriptorType keyDesc = desc.getKeyDescriptor().get(0); + assertThat(keyDesc, notNullValue()); + assertThat(keyDesc.getUse(), equalTo(KeyTypes.SIGNING)); + NodeList cert = keyDesc.getKeyInfo().getElementsByTagNameNS(XMLSignature.XMLNS, "X509Certificate"); + assertThat("KeyDescriptor.Signing.Cert existence", cert.getLength(), is(1)); } private void assertProviderInfo(Map info, String id, String name) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java index 75818b2427a..02a2cdb3d48 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/AbstractClientTest.java @@ -27,6 +27,7 @@ import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.AbstractAuthTest; +import org.keycloak.testsuite.Assert; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.events.EventsListenerProviderFactory; import org.keycloak.testsuite.util.AdminEventPaths; @@ -70,9 +71,16 @@ public abstract class AbstractClientTest extends AbstractAuthTest { // returns UserRepresentation retrieved from server, with all fields, including id protected UserRepresentation getFullUserRep(String userName) { + // the search returns all users who has userName contained in their username. List results = testRealmResource().users().search(userName, null, null, null, null, null); - if (results.size() != 1) throw new RuntimeException("Did not find single user with username " + userName); - return results.get(0); + UserRepresentation result = null; + for (UserRepresentation user : results) { + if (userName.equals(user.getUsername())) { + result = user; + } + } + Assert.assertNotNull("Did not find user with username " + userName, result); + return result; } protected String createOidcClient(String name) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java index 626c0d4d516..4328c8f9994 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/InstallationTest.java @@ -21,13 +21,11 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.keycloak.admin.client.resource.ClientResource; -import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.keycloak.testsuite.auth.page.AuthRealm.TEST; +import static org.junit.Assert.assertThat; +import static org.hamcrest.Matchers.*; /** * Test getting the installation/configuration files for OIDC and SAML. @@ -71,7 +69,7 @@ public class InstallationTest extends AbstractClientTest { public void testOidcJBossXml() { String xml = oidcClient.getInstallationProvider("keycloak-oidc-jboss-subsystem"); assertOidcInstallationConfig(xml); - assertTrue(xml.contains("")); - assertTrue(xml.contains(SAML_NAME)); - assertTrue(xml.contains(ApiUtil.findActiveKey(testRealmResource()).getCertificate())); - assertTrue(xml.contains(samlUrl())); + assertThat(xml, containsString("")); + assertThat(xml, containsString(SAML_NAME)); + assertThat(xml, not(containsString(ApiUtil.findActiveKey(testRealmResource()).getCertificate()))); + assertThat(xml, containsString(samlUrl())); } @Test public void testSamlMetadataSpDescriptor() { String xml = samlClient.getInstallationProvider("saml-sp-descriptor"); - assertTrue(xml.contains("Stian Thorgersen @@ -47,6 +49,9 @@ public class LogoutTest extends TestRealmKeycloakTest { @Page protected LoginPage loginPage; + @Page + protected AccountManagement accountManagementPage; + @Override public void configureTestRealm(RealmRepresentation testRealm) { } @@ -130,4 +135,45 @@ public class LogoutTest extends TestRealmKeycloakTest { events.expectLogin().session(sessionId3).removeDetail(Details.USERNAME).assertEvent(); } + //KEYCLOAK-2741 + @Test + public void logoutWithRememberMe() { + setRememberMe(true); + + try { + loginPage.open(); + assertFalse(loginPage.isRememberMeChecked()); + loginPage.setRememberMe(true); + assertTrue(loginPage.isRememberMeChecked()); + loginPage.login("test-user@localhost", "password"); + + String sessionId = events.expectLogin().assertEvent().getSessionId(); + + // Expire session + testingClient.testing().removeUserSession("test", sessionId); + + // Assert rememberMe checked and username/email prefilled + loginPage.open(); + assertTrue(loginPage.isRememberMeChecked()); + assertEquals("test-user@localhost", loginPage.getUsername()); + + loginPage.login("test-user@localhost", "password"); + + //log out + appPage.openAccount(); + accountManagementPage.signOut(); + // Assert rememberMe not checked nor username/email prefilled + assertTrue(loginPage.isCurrent()); + assertFalse(loginPage.isRememberMeChecked()); + assertNotEquals("test-user@localhost", loginPage.getUsername()); + } finally { + setRememberMe(false); + } + } + + private void setRememberMe(boolean enabled) { + RealmRepresentation rep = adminClient.realm("test").toRepresentation(); + rep.setRememberMe(enabled); + adminClient.realm("test").update(rep); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java index cac8a27b313..642a872176a 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/JavaKeystoreKeyProviderTest.java @@ -22,15 +22,10 @@ import org.jboss.arquillian.graphene.page.Page; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; -import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.MultivaluedHashMap; -import org.keycloak.common.util.PemUtils; -import org.keycloak.keys.Attributes; -import org.keycloak.keys.GeneratedRsaKeyProviderFactory; import org.keycloak.keys.JavaKeystoreKeyProviderFactory; import org.keycloak.keys.KeyMetadata; import org.keycloak.keys.KeyProvider; -import org.keycloak.keys.RsaKeyProviderFactory; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.ErrorRepresentation; import org.keycloak.representations.idm.KeysMetadataRepresentation; @@ -45,9 +40,6 @@ import javax.ws.rs.core.Response; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; -import java.security.KeyPair; -import java.security.cert.Certificate; -import java.security.interfaces.RSAPublicKey; import java.util.List; import static org.junit.Assert.*; @@ -123,7 +115,7 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest { rep.getConfig().putSingle("keystore", "/nosuchfile"); Response response = adminClient.realm("test").components().add(rep); - assertErrror(response, "Failed to load keys"); + assertErrror(response, "Failed to load keys. File not found on server."); } @Test @@ -132,7 +124,7 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest { rep.getConfig().putSingle("keystore", "invalid"); Response response = adminClient.realm("test").components().add(rep); - assertErrror(response, "Failed to load keys"); + assertErrror(response, "Failed to load keys. File not found on server."); } @Test @@ -141,7 +133,7 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest { rep.getConfig().putSingle("keyAlias", "invalid"); Response response = adminClient.realm("test").components().add(rep); - assertErrror(response, "Failed to load keys"); + assertErrror(response, "Failed to load keys. Error creating X509v1Certificate."); } @Test @@ -150,7 +142,7 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest { rep.getConfig().putSingle("keyPassword", "invalid"); Response response = adminClient.realm("test").components().add(rep); - assertErrror(response, "Failed to load keys"); + assertErrror(response, "Failed to load keys. Keystore on server can not be recovered."); } protected void assertErrror(Response response, String error) { @@ -159,7 +151,7 @@ public class JavaKeystoreKeyProviderTest extends AbstractKeycloakTest { } ErrorRepresentation errorRepresentation = response.readEntity(ErrorRepresentation.class); - assertEquals(error, errorRepresentation.getErrorMessage()); + assertTrue(errorRepresentation.getErrorMessage().startsWith(error)); } protected ComponentRepresentation createRep(String name, long priority) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java index 2dc3506bfc0..5bfaef2b2ab 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/keys/RsaKeyProviderTest.java @@ -20,17 +20,13 @@ package org.keycloak.testsuite.keys; import org.jboss.arquillian.graphene.page.Page; import org.junit.Rule; import org.junit.Test; -import org.keycloak.RSATokenVerifier; -import org.keycloak.common.VerificationException; import org.keycloak.common.util.CertificateUtils; import org.keycloak.common.util.KeyUtils; import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.PemUtils; import org.keycloak.keys.Attributes; -import org.keycloak.keys.GeneratedRsaKeyProviderFactory; import org.keycloak.keys.KeyMetadata; import org.keycloak.keys.KeyProvider; -import org.keycloak.keys.RsaKeyProvider; import org.keycloak.keys.RsaKeyProviderFactory; import org.keycloak.representations.idm.ComponentRepresentation; import org.keycloak.representations.idm.ErrorRepresentation; @@ -40,16 +36,11 @@ import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.pages.AppPage; -import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.LoginPage; -import org.keycloak.testsuite.util.OAuthClient; -import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.Response; import java.security.KeyPair; -import java.security.PublicKey; import java.security.cert.Certificate; -import java.security.cert.X509Certificate; import java.util.List; import static org.junit.Assert.*; diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java index d9c041c14da..72d0c70c749 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java @@ -36,9 +36,15 @@ import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation; import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.representations.idm.ClientTemplateRepresentation; +import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.representations.idm.RoleRepresentation; -import static org.keycloak.testsuite.Assert.*; +import static org.keycloak.testsuite.Assert.assertEquals; +import static org.keycloak.testsuite.Assert.assertFalse; +import static org.keycloak.testsuite.Assert.assertNames; +import static org.keycloak.testsuite.Assert.assertTrue; +import static org.keycloak.testsuite.Assert.fail; import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER; /** @@ -71,12 +77,14 @@ public class MigrationTest extends AbstractKeycloakTest { testMigrationTo2_0_0(); testMigrationTo2_1_0(); testMigrationTo2_2_0(); + testMigrationTo2_3_0(); } @Test @Migration(versionFrom = "2.2.1.Final") public void migration2_2_1Test() { testMigratedData(); + testMigrationTo2_3_0(); } private void testMigratedData() { @@ -120,6 +128,13 @@ public class MigrationTest extends AbstractKeycloakTest { //MigrateTo2_2_0#migrateRolePolicies is not relevant any more } + /** + * @see org.keycloak.migration.migrators.MigrateTo2_3_0 + */ + private void testMigrationTo2_3_0() { + testUpdateProtocolMappers(masterRealm, migrationRealm); + } + private void testAuthorizationServices(RealmResource... realms) { for (RealmResource realm : realms) { //test setup of authorization services @@ -177,4 +192,26 @@ public class MigrationTest extends AbstractKeycloakTest { } } } + + private void testUpdateProtocolMappers(RealmResource... realms) { + for (RealmResource realm : realms) { + for (ClientRepresentation client : realm.clients().findAll()) { + for (ProtocolMapperRepresentation protocolMapper : client.getProtocolMappers()) { + testUpdateProtocolMapper(protocolMapper); + } + } + for (ClientTemplateRepresentation clientTemlate : realm.clientTemplates().findAll()) { + for (ProtocolMapperRepresentation protocolMapper : clientTemlate.getProtocolMappers()) { + testUpdateProtocolMapper(protocolMapper); + } + } + } + } + + private void testUpdateProtocolMapper(ProtocolMapperRepresentation protocolMapper) { + if (protocolMapper.getConfig().get("id.token.claim") != null) { + assertEquals("ProtocolMapper's config should contain key 'userinfo.token.claim'.", + protocolMapper.getConfig().get("id.token.claim"), protocolMapper.getConfig().get("userinfo.token.claim")); + } + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java index eaeeafd9c13..7115f7481e6 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java @@ -39,6 +39,7 @@ import org.keycloak.events.Errors; import org.keycloak.jose.jws.JWSHeader; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInputException; +import org.keycloak.models.Constants; import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.UserModel; import org.keycloak.models.utils.KeycloakModelUtils; @@ -56,6 +57,7 @@ import org.keycloak.representations.idm.RoleRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.arquillian.AuthServerTestEnricher; import org.keycloak.testsuite.util.ClientBuilder; import org.keycloak.testsuite.util.ClientManager; import org.keycloak.testsuite.util.OAuthClient; @@ -75,10 +77,8 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import java.io.IOException; import java.net.URI; -import java.util.Arrays; import java.util.LinkedList; import java.util.List; -import java.util.Map; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.greaterThanOrEqualTo; @@ -89,14 +89,9 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson; import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId; -import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId; import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsername; import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsernameId; import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT; -import static org.keycloak.testsuite.util.ProtocolMapperUtil.createAddressMapper; -import static org.keycloak.testsuite.util.ProtocolMapperUtil.createClaimMapper; -import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedClaim; -import static org.keycloak.testsuite.util.ProtocolMapperUtil.createHardcodedRole; import static org.keycloak.testsuite.util.ProtocolMapperUtil.createRoleNameMapper; /** @@ -200,6 +195,30 @@ public class AccessTokenTest extends AbstractKeycloakTest { } + // KEYCLOAK-3692 + @Test + public void accessTokenWrongCode() throws Exception { + oauth.clientId(Constants.ADMIN_CONSOLE_CLIENT_ID); + oauth.redirectUri(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth/admin/test/console/nosuch.html"); + oauth.openLoginForm(); + + String actionUrl = driver.getPageSource().split("action=\"")[1].split("\"")[0].replaceAll("&", "&"); + actionUrl = actionUrl.replaceFirst("&execution=.*", ""); + + String loginPageCode = actionUrl.split("code=")[1].split("&")[0]; + + driver.navigate().to(actionUrl); + + oauth.fillLoginForm("test-user@localhost", "password"); + + events.expectLogin().client(Constants.ADMIN_CONSOLE_CLIENT_ID).detail(Details.REDIRECT_URI, AuthServerTestEnricher.getAuthServerContextRoot() + "/auth/admin/test/console/nosuch.html").assertEvent(); + + OAuthClient.AccessTokenResponse response = oauth.doAccessTokenRequest(loginPageCode, null); + + assertEquals(400, response.getStatusCode()); + assertNull(response.getRefreshToken()); + } + @Test public void accessTokenInvalidClientCredentials() throws Exception { oauth.doLogin("test-user@localhost", "password"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/META-INF/context.xml new file mode 100644 index 00000000000..8d1c0d63e96 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/META-INF/context.xml @@ -0,0 +1,20 @@ + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/WEB-INF/jetty-web.xml new file mode 100644 index 00000000000..8c59313878f --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/WEB-INF/jetty-web.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/WEB-INF/web.xml new file mode 100644 index 00000000000..71fd5cdb324 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal-noconf/WEB-INF/web.xml @@ -0,0 +1,76 @@ + + + + + + customer-portal-noconf + + + Servlet + org.keycloak.testsuite.adapter.servlet.CustomerServletNoConf + + + Error Servlet + org.keycloak.testsuite.adapter.servlet.ErrorServlet + + + + Servlet + /* + + + + Error Servlet + /error.html + + + + + Users + /* + + + user + + + + + Errors + /error.html + + + + + KEYCLOAK + demo + + /error.html + /error.html + + + + + admin + + + user + + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml index 2ea7ff73c6f..68a1ac8dc7e 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/bad-assertion-sales-post-sig/WEB-INF/keycloak-saml.xml @@ -15,7 +15,9 @@ ~ limitations under the License. --> - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-post-noidpkey/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-post-noidpkey/WEB-INF/keystore.jks new file mode 100644 index 00000000000..4daad218a3f Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-post-noidpkey/WEB-INF/keystore.jks differ diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-noidpkey/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-noidpkey/WEB-INF/keycloak-saml.xml new file mode 100644 index 00000000000..73b8f4e3893 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-noidpkey/WEB-INF/keycloak-saml.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-noidpkey/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-noidpkey/WEB-INF/keystore.jks new file mode 100644 index 00000000000..4daad218a3f Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-noidpkey/WEB-INF/keystore.jks differ diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-opt-noidpkey/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-opt-noidpkey/WEB-INF/keycloak-saml.xml new file mode 100644 index 00000000000..a12ddfe84e5 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-opt-noidpkey/WEB-INF/keycloak-saml.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-opt-noidpkey/WEB-INF/keystore.jks b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-opt-noidpkey/WEB-INF/keystore.jks new file mode 100644 index 00000000000..4daad218a3f Binary files /dev/null and b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig-redir-opt-noidpkey/WEB-INF/keystore.jks differ diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig/WEB-INF/keycloak-saml.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig/WEB-INF/keycloak-saml.xml index 1f926b77853..a883abf5025 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig/WEB-INF/keycloak-saml.xml +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/keycloak-saml/employee-sig/WEB-INF/keycloak-saml.xml @@ -15,7 +15,9 @@ ~ limitations under the License. --> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + urn:oasis:names:tc:SAML:2.0:nameid-format:persistent + urn:oasis:names:tc:SAML:2.0:nameid-format:transient + urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified + urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + + + + + + hAoy_sBtpu6FdRVCk7ykihF6Ug-o0pKPK3LN9RYkeqs + + + MIICmzCCAYMCBgFUYnC0OjANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYwNDI5MTQzMjEzWhcNMjYwNDI5MTQzMzUzWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCN25AW1poMEZRbuMAHG58AThZmCwMV6/Gcui4mjGacRFyudgqzLjQ2rxpoW41JAtLjbjeAhuWvirUcFVcOeS3gM/ZC27qCpYighAcylZz6MYocnEe1+e8rPPk4JlID6Wv62dgu+pL/vYsQpRhvD3Y2c/ytgr5D32xF+KnzDehUy5BSyzypvu12Wq9mS5vK5tzkN37EjkhpY2ZxaXPubjDIITCAL4Q8M/m5IlacBaUZbzI4AQrHnMP1O1IH2dHSWuMiBe+xSDTco72PmuYPJKTV4wQdeBUIkYbfLc4RxVmXEvgkQgyW86EoMPxlWJpj7+mTIR+l+2thZPr/VgwTs82rAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAA/Ip/Hi8RoVu5ouaFFlc5whT7ltuK8slfLGW4tM4vJXhInYwsqIRQKBNDYW/64xle3eII4u1yAH1OYRRwEs7Em1pr4QuFuTY1at+aE0sE46XDlyESI0txJjWxYoT133vM0We2pj1b2nxgU30rwjKA3whnKEfTEYT/n3JBSqNggy6l8ZGw/oPSgvPaR4+xeB1tfQFC4VrLoYKoqH6hAL530nKxL+qV8AIfL64NDEE8ankIAEDAAFe8x3CPUfXR/p4KOANKkpz8ieQaHDb1eITkAwUwjESj6UF9D1aePlhWls/HX0gujFXtWfWfrJ8CU/ogwlH8y1jgRuLjFQYZk6llc= + + + + + + + FJ86GcF3jTbNLOco4NvZkUCIUmfYCqoqtOQeMfbhNlE + + + MIIBnDCCAQUCBgFYKXKsPTANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAlzYW1sLWRlbW8wHhcNMTYxMTAzMDkwNzEwWhcNMjYxMTAzMDkwODUwWjAUMRIwEAYDVQQDDAlzYW1sLWRlbW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKtWsK5O0CtuBpnMvWG+HTG0vmZzujQ2o9WdheQu+BzCILcGMsbDW0YQaglpcO5JpGWWhubnckGGPHfdQ2/7nP9QwbiTK0FbGF41UqcvoaCqU1psxoV88s8IXyQCAqeyLv00yj6foqdJjxh5SZ5z+na+M7Y2OxIBVxYRAxWEnfUvAgMBAAEwDQYJKoZIhvcNAQELBQADgYEAhetvOU8TyqfZF5jpv0IcrviLl/DoFrbjByeHR+pu/vClcAOjL/u7oQELuuTfNsBI4tpexUj5G8q/YbEz0gk7idfLXrAUVcsR73oTngrhRfwUSmPrjjK0kjcRb6HL9V/+wh3R/6mEd59U08ExT8N38rhmn0CI3ehMdebReprP7U8= + + + + + + diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/admin-test/saml-idp-metadata.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/admin-test/saml-idp-metadata.xml index 2bcfc211ecc..f28e206717b 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/admin-test/saml-idp-metadata.xml +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/admin-test/saml-idp-metadata.xml @@ -1,6 +1,7 @@ diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-1.9.8.Final.json b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-1.9.8.Final.json index 4194a95abf9..ba783b4d995 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-1.9.8.Final.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-1.9.8.Final.json @@ -829,6 +829,7 @@ }, { "id" : "d89c5b0f-bee6-4a97-86b1-118efa21e508", "clientId" : "master-test-client", + "name" : "master-test-client", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", @@ -1898,6 +1899,7 @@ }, { "id" : "d8262b3f-02e4-409e-97fc-ee5532e0801e", "clientId" : "migration-test-client", + "name" : "migration-test-client", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.2.1.Final.json b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.2.1.Final.json index 7011d1b504f..db1c768f242 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.2.1.Final.json +++ b/testsuite/integration-arquillian/tests/base/src/test/resources/migration-test/migration-realm-2.2.1.Final.json @@ -933,6 +933,7 @@ }, { "id" : "6268e266-346b-46ba-8408-fe17b5792b10", "clientId" : "master-test-client", + "name" : "master-test-client", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", @@ -2085,6 +2086,7 @@ }, { "id" : "f66de6ed-4fd8-47b6-a2db-85ab8ed88874", "clientId" : "migration-test-client", + "name" : "migration-test-client", "surrogateAuthRequired" : false, "enabled" : true, "clientAuthenticatorType" : "client-secret", diff --git a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java index 79092cd7d87..b8cbf4dfe02 100644 --- a/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java +++ b/testsuite/integration-arquillian/tests/other/console/src/main/java/org/keycloak/testsuite/console/page/clients/settings/ClientSettingsForm.java @@ -223,6 +223,7 @@ public class ClientSettingsForm extends CreateClientForm { public static final String SAML_FORCE_POST_BINDING = "saml.force.post.binding"; public static final String SAML_MULTIVALUED_ROLES = "saml.multivalued.roles"; public static final String SAML_SERVER_SIGNATURE = "saml.server.signature"; + public static final String SAML_SERVER_SIGNATURE_KEYINFO_EXT = "saml.server.signature.keyinfo.ext"; public static final String SAML_SIGNATURE_ALGORITHM = "saml.signature.algorithm"; public static final String SAML_ASSERTION_CONSUMER_URL_POST = "saml_assertion_consumer_url_post"; public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT = "saml_assertion_consumer_url_redirect"; @@ -236,6 +237,8 @@ public class ClientSettingsForm extends CreateClientForm { private OnOffSwitch samlAuthnStatement; @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlServerSignature']]") private OnOffSwitch samlServerSignature; + @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlServerSignatureEnableKeyInfoExtension']]") + private OnOffSwitch samlServerSignatureKeyInfoExt; @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='samlAssertionSignature']]") private OnOffSwitch samlAssertionSignature; @FindBy(id = "signatureAlgorithm") @@ -277,6 +280,7 @@ public class ClientSettingsForm extends CreateClientForm { if (samlServerSignature.isOn() || samlAssertionSignature.isOn()) { signatureAlgorithm.selectByVisibleText(attributes.get(SAML_SIGNATURE_ALGORITHM)); canonicalization.selectByValue("string:" + attributes.get(SAML_SIGNATURE_CANONICALIZATION_METHOD)); + samlServerSignatureKeyInfoExt.setOn("true".equals(attributes.get(SAML_SERVER_SIGNATURE_KEYINFO_EXT))); } samlEncrypt.setOn("true".equals(attributes.get(SAML_ENCRYPT))); samlClientSignature.setOn("true".equals(attributes.get(SAML_CLIENT_SIGNATURE))); diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml index ea63ca4bb17..4f4f42ddd6f 100755 --- a/testsuite/integration-arquillian/tests/pom.xml +++ b/testsuite/integration-arquillian/tests/pom.xml @@ -458,42 +458,46 @@ - - maven-dependency-plugin - - - unpack-migrated-auth-server-jboss - generate-resources - - unpack - - - - - org.keycloak.testsuite - integration-arquillian-migration-server - ${project.version} - zip - - - ${containers.home} - true - - - - - - maven-surefire-plugin - - - ${migrated.auth.server.version} - true - ${containers.home}/keycloak-${migrated.auth.server.version} - ${migration.import.props.previous} - - - + + + + maven-dependency-plugin + + + unpack-migrated-auth-server-jboss + generate-resources + + unpack + + + + + org.keycloak.testsuite + integration-arquillian-migration-server + ${project.version} + zip + + + ${containers.home} + true + + + + + + maven-surefire-plugin + + + ${migrated.auth.server.version} + true + ${containers.home}/keycloak-${migrated.auth.server.version} + ${migration.import.props.previous} + + + + + diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index d1311b59eaf..873d99ddfe4 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -120,7 +120,7 @@ org.keycloak - keycloak-server-spi + keycloak-server-spi-private org.keycloak diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java index dbf30e9960e..09ddb39a3a9 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/ImportIdentityProviderTest.java @@ -193,6 +193,7 @@ public class ImportIdentityProviderTest extends AbstractIdentityProviderModelTes assertEquals(true, config.isPostBindingAuthnRequest()); assertEquals(true, config.isPostBindingResponse()); assertEquals(true, config.isValidateSignature()); + assertEquals(false, config.isAddExtensionsElementWithKeyInfo()); } private void assertOidcIdentityProviderConfig(IdentityProviderModel identityProvider) { diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java index cafe113c758..6eccb21d239 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/FederatedStorageExportImportTest.java @@ -79,7 +79,7 @@ public class FederatedStorageExportImportTest { protected PasswordHashProvider getHashProvider(KeycloakSession session, PasswordPolicy policy) { PasswordHashProvider hash = session.getProvider(PasswordHashProvider.class, policy.getHashAlgorithm()); if (hash == null) { - return session.getProvider(PasswordHashProvider.class, HashAlgorithmPasswordPolicyProviderFactory.DEFAULT_VALUE); + return session.getProvider(PasswordHashProvider.class, PasswordPolicy.HASH_ALGORITHM_DEFAULT); } return hash; } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java index 431bff6ff40..2db24addcaa 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionInitializerTest.java @@ -34,7 +34,7 @@ import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionProvider; import org.keycloak.models.UserSessionProviderFactory; import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.keycloak.services.managers.UserManager; +import org.keycloak.models.UserManager; import org.keycloak.services.managers.UserSessionManager; import org.keycloak.testsuite.rule.KeycloakRule; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java index 074438f2bf3..80f663f8460 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionPersisterProviderTest.java @@ -33,7 +33,7 @@ import org.keycloak.models.session.UserSessionPersisterProvider; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.services.managers.ClientManager; import org.keycloak.services.managers.RealmManager; -import org.keycloak.services.managers.UserManager; +import org.keycloak.models.UserManager; import org.keycloak.testsuite.rule.KeycloakRule; import java.util.ArrayList; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java index 013f16c57f3..b9f4f2aedaf 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderOfflineTest.java @@ -34,7 +34,7 @@ import org.keycloak.models.session.UserSessionPersisterProvider; import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.services.managers.ClientManager; import org.keycloak.services.managers.RealmManager; -import org.keycloak.services.managers.UserManager; +import org.keycloak.models.UserManager; import org.keycloak.services.managers.UserSessionManager; import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.LoggingRule; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java index d75e467c0f8..f2fc3aab0d9 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserSessionProviderTest.java @@ -31,7 +31,7 @@ import org.keycloak.models.UserLoginFailureModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; import org.keycloak.protocol.oidc.OIDCLoginProtocol; -import org.keycloak.services.managers.UserManager; +import org.keycloak.models.UserManager; import org.keycloak.testsuite.rule.KeycloakRule; import java.util.Arrays; diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-assertion-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/bad-assertion-signed-post/WEB-INF/keycloak-saml.xml index 16f78d99b6a..bc0e9aff674 100755 --- a/testsuite/integration/src/test/resources/keycloak-saml/bad-assertion-signed-post/WEB-INF/keycloak-saml.xml +++ b/testsuite/integration/src/test/resources/keycloak-saml/bad-assertion-signed-post/WEB-INF/keycloak-saml.xml @@ -15,7 +15,9 @@ ~ limitations under the License. --> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + element? This optimizes validation of the signature as the validating party uses a single key instead of trying every known key for validation. sign-assertions=Sign Assertions sign-assertions.tooltip=Should assertions inside SAML documents be signed? This setting isn't needed if document is already being signed. signature-algorithm=Signature Algorithm @@ -506,8 +508,8 @@ force-authentication=Force Authentication identity-provider.force-authentication.tooltip=Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context. validate-signature=Validate Signature saml.validate-signature.tooltip=Enable/disable signature validation of SAML responses. -validating-x509-certificate=Validating X509 Certificate -validating-x509-certificate.tooltip=The certificate in PEM format that must be used to check for signatures. +validating-x509-certificate=Validating X509 Certificates +validating-x509-certificate.tooltip=The certificate in PEM format that must be used to check for signatures. Multiple certificates can be entered, separated by comma (,). saml.import-from-url.tooltip=Import metadata from a remote IDP SAML entity descriptor. social.client-id.tooltip=The client identifier registered with the identity provider. social.client-secret.tooltip=The client secret registered with the identity provider. diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js index 4503b2f2c82..624e9a5733b 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js @@ -860,6 +860,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates, $scope.samlAuthnStatement = false; $scope.samlMultiValuedRoles = false; $scope.samlServerSignature = false; + $scope.samlServerSignatureEnableKeyInfoExtension = false; $scope.samlAssertionSignature = false; $scope.samlClientSignature = false; $scope.samlEncrypt = false; @@ -908,6 +909,13 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates, } } + if ($scope.client.attributes["saml.server.signature.keyinfo.ext"]) { + if ($scope.client.attributes["saml.server.signature.keyinfo.ext"] == "true") { + $scope.samlServerSignatureEnableKeyInfoExtension = true; + } else { + $scope.samlServerSignatureEnableKeyInfoExtension = false; + } + } if ($scope.client.attributes["saml.assertion.signature"]) { if ($scope.client.attributes["saml.assertion.signature"] == "true") { $scope.samlAssertionSignature = true; @@ -1115,7 +1123,11 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates, $scope.client.attributes["saml.server.signature"] = "true"; } else { $scope.client.attributes["saml.server.signature"] = "false"; - + } + if ($scope.samlServerSignatureEnableKeyInfoExtension == true) { + $scope.client.attributes["saml.server.signature.keyinfo.ext"] = "true"; + } else { + $scope.client.attributes["saml.server.signature.keyinfo.ext"] = "false"; } if ($scope.samlAssertionSignature == true) { $scope.client.attributes["saml.assertion.signature"] = "true"; diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html index 8601770dba1..6b0f0a6a47d 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html @@ -118,7 +118,7 @@
- +
@@ -131,6 +131,13 @@
{{:: 'sign-documents.tooltip' | translate}} +
+ +
+ +
+ {{:: 'sign-documents-redirect-enable-key-info-ext.tooltip' | translate}} +
diff --git a/travis-run-tests.sh b/travis-run-tests.sh new file mode 100755 index 00000000000..fe7c88e674c --- /dev/null +++ b/travis-run-tests.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +if [ $1 == "old" ]; then + mvn test -B -f testsuite/integration +fi + +if [ $1 == "group1" ]; then + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.a**.*Test + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.b**.*Test + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.c**.*Test +fi + +if [ $1 == "group2" ]; then + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.d**.*Test + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.e**.*Test + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.f**.*Test + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.i**.*Test +fi + +if [ $1 == "group3" ]; then + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.k**.*Test + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.m**.*Test + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.testsuite.o**.*Test + mvn test -B -f testsuite/integration-arquillian/tests/base -Dtest=org.keycloak.*Test +fi + +if [ $1 == "adapter" ]; then + mvn test -B -f testsuite/integration-arquillian/tests/other/adapters +fi + diff --git a/wildfly/adduser/pom.xml b/wildfly/adduser/pom.xml index b33eb5fdb7c..6a827f48f05 100755 --- a/wildfly/adduser/pom.xml +++ b/wildfly/adduser/pom.xml @@ -34,6 +34,10 @@ org.keycloak keycloak-server-spi + + org.keycloak + keycloak-server-spi-private + org.keycloak keycloak-common diff --git a/wildfly/extensions/pom.xml b/wildfly/extensions/pom.xml index 0cafd9c5fe8..9b677db509b 100755 --- a/wildfly/extensions/pom.xml +++ b/wildfly/extensions/pom.xml @@ -45,6 +45,11 @@ keycloak-server-spi provided + + org.keycloak + keycloak-server-spi-private + provided + org.keycloak keycloak-services diff --git a/wildfly/server-subsystem/pom.xml b/wildfly/server-subsystem/pom.xml index 7710f9fc7bb..03ac1a7e019 100755 --- a/wildfly/server-subsystem/pom.xml +++ b/wildfly/server-subsystem/pom.xml @@ -108,7 +108,7 @@ org.keycloak - keycloak-server-spi + keycloak-server-spi-private provided