From 23e3bc5f8f1f87bd4156dc72491e8cf02afca4f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Barto=C5=A1?= Date: Mon, 14 Jun 2021 14:43:07 +0200 Subject: [PATCH] KEYCLOAK-18466 Configure HTTP client timeouts for adapters --- .../keycloak/adapters/HttpClientBuilder.java | 8 + .../KeycloakDeploymentBuilderTest.java | 17 + .../test/resources/keycloak-http-client.json | 8 + .../subsystem/saml/as7/Constants.java | 4 + .../saml/as7/HttpClientDefinition.java | 14 +- .../saml/as7/KeycloakSamlExtension.java | 4 +- .../saml/as7/LocalDescriptions.properties | 4 +- .../schema/wildfly-keycloak-saml_1_4.xsd | 580 ++++++++++++++++++ ...systemParsingAllowedClockSkewTestCase.java | 2 +- .../saml/as7/SubsystemParsingTestCase.java | 2 +- ...oak-saml-1.3.xml => keycloak-saml-1.4.xml} | 18 +- .../cloned/AdapterHttpClientConfig.java | 27 +- .../adapters/cloned/HttpClientBuilder.java | 8 + .../keycloak/adapters/saml/config/IDP.java | 20 + .../saml/config/parsers/HttpClientParser.java | 4 + .../parsers/KeycloakSamlAdapterV1QNames.java | 5 +- .../schema/keycloak_saml_adapter_1_13.xsd | 550 +++++++++++++++++ .../KeycloakSamlAdapterXMLParserTest.java | 147 ++--- ...keycloak-saml-wth-http-client-settings.xml | 4 +- .../adapter/saml/extension/Constants.java | 4 + .../saml/extension/HttpClientDefinition.java | 14 +- .../saml/extension/KeycloakSamlExtension.java | 4 +- .../extension/LocalDescriptions.properties | 4 +- .../schema/wildfly-keycloak-saml_1_4.xsd | 580 ++++++++++++++++++ .../keycloak-saml-adapter.xml | 2 +- ...systemParsingAllowedClockSkewTestCase.java | 4 +- .../extension/SubsystemParsingTestCase.java | 4 +- ...oak-saml-1.3.xml => keycloak-saml-1.4.xml} | 18 +- .../adapters/config/AdapterConfig.java | 21 + .../config/AdapterHttpClientConfig.java | 9 + .../java/org/keycloak/JsonParserTest.java | 4 + core/src/test/resources/keycloak.json | 4 +- .../saml/common/util/StaxParserUtil.java | 24 + 33 files changed, 2010 insertions(+), 112 deletions(-) create mode 100644 adapters/oidc/adapter-core/src/test/resources/keycloak-http-client.json create mode 100644 adapters/saml/as7-eap6/subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_4.xsd rename adapters/saml/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/{keycloak-saml-1.3.xml => keycloak-saml-1.4.xml} (87%) create mode 100644 adapters/saml/core/src/main/resources/schema/keycloak_saml_adapter_1_13.xsd create mode 100644 adapters/saml/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_4.xsd rename adapters/saml/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/{keycloak-saml-1.3.xml => keycloak-saml-1.4.xml} (87%) mode change 100755 => 100644 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 da1fc3851c9..42dfa1bf678 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 @@ -376,6 +376,14 @@ public class HttpClientBuilder { configureProxyForAuthServerIfProvided(adapterConfig); + if (socketTimeout == -1 && adapterConfig.getSocketTimeout() > 0) { + socketTimeout(adapterConfig.getSocketTimeout(), TimeUnit.MILLISECONDS); + } + + if (establishConnectionTimeout == -1 && adapterConfig.getConnectionTimeout() > 0) { + establishConnectionTimeout(adapterConfig.getConnectionTimeout(), TimeUnit.MILLISECONDS); + } + return build(); } diff --git a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java index 957db002361..851b008163f 100644 --- a/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java +++ b/adapters/oidc/adapter-core/src/test/java/org/keycloak/adapters/KeycloakDeploymentBuilderTest.java @@ -17,7 +17,10 @@ package org.keycloak.adapters; +import org.apache.http.client.HttpClient; import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; +import org.apache.http.params.CoreConnectionPNames; +import org.hamcrest.CoreMatchers; import org.junit.Test; import org.keycloak.adapters.authentication.ClientIdAndSecretCredentialsProvider; import org.keycloak.adapters.authentication.JWTClientCredentialsProvider; @@ -29,6 +32,7 @@ import org.keycloak.common.enums.SslRequired; import org.keycloak.common.util.PemUtils; import org.keycloak.enums.TokenStore; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -101,5 +105,18 @@ public class KeycloakDeploymentBuilderTest { assertEquals(JWTClientSecretCredentialsProvider.PROVIDER_ID, deployment.getClientAuthenticator().getId()); } + @Test + public void loadHttpClientTimeoutConfiguration() { + KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(getClass().getResourceAsStream("/keycloak-http-client.json")); + assertThat(deployment, CoreMatchers.notNullValue()); + HttpClient client = deployment.getClient(); + assertThat(client, CoreMatchers.notNullValue()); + + long socketTimeout = client.getParams().getIntParameter(CoreConnectionPNames.SO_TIMEOUT, -2); + long connectionTimeout = client.getParams().getIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, -2); + + assertThat(socketTimeout, CoreMatchers.is(2000L)); + assertThat(connectionTimeout, CoreMatchers.is(6000L)); + } } diff --git a/adapters/oidc/adapter-core/src/test/resources/keycloak-http-client.json b/adapters/oidc/adapter-core/src/test/resources/keycloak-http-client.json new file mode 100644 index 00000000000..b9508d9fb12 --- /dev/null +++ b/adapters/oidc/adapter-core/src/test/resources/keycloak-http-client.json @@ -0,0 +1,8 @@ +{ + "realm": "demo", + "resource": "customer-portal", + "auth-server-url": "https://localhost:8443/auth", + "public-client": true, + "socket-timeout": 2000, + "connection-timeout": 6000 +} \ No newline at end of file diff --git a/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Constants.java b/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Constants.java index 1ca02718db5..ebec74b3b2a 100755 --- a/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Constants.java +++ b/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Constants.java @@ -91,6 +91,8 @@ public class Constants { static final String PROXY_URL = "proxyUrl"; static final String TRUSTSTORE = "truststore"; static final String TRUSTSTORE_PASSWORD = "truststorePassword"; + static final String SOCKET_TIMEOUT = "socketTimeout"; + static final String CONNECTION_TIMEOUT = "connectionTimeout"; } static class XML { @@ -170,5 +172,7 @@ public class Constants { static final String PROXY_URL = "proxyUrl"; static final String TRUSTSTORE = "truststore"; static final String TRUSTSTORE_PASSWORD = "truststorePassword"; + static final String SOCKET_TIMEOUT = "socketTimeout"; + static final String CONNECTION_TIMEOUT = "connectionTimeout"; } } diff --git a/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/HttpClientDefinition.java b/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/HttpClientDefinition.java index b97f1d21d3c..1881f497a35 100644 --- a/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/HttpClientDefinition.java +++ b/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/HttpClientDefinition.java @@ -78,8 +78,20 @@ abstract class HttpClientDefinition { .setAllowExpression(true) .build(); + private static final SimpleAttributeDefinition SOCKET_TIMEOUT = + new SimpleAttributeDefinitionBuilder(Constants.Model.SOCKET_TIMEOUT, ModelType.LONG, true) + .setXmlName(Constants.XML.SOCKET_TIMEOUT) + .setAllowExpression(true) + .build(); + + private static final SimpleAttributeDefinition CONNECTION_TIMEOUT = + new SimpleAttributeDefinitionBuilder(Constants.Model.CONNECTION_TIMEOUT, ModelType.LONG, true) + .setXmlName(Constants.XML.CONNECTION_TIMEOUT) + .setAllowExpression(true) + .build(); + static final SimpleAttributeDefinition[] ATTRIBUTES = {ALLOW_ANY_HOSTNAME, CLIENT_KEYSTORE, CLIENT_KEYSTORE_PASSWORD, - CONNECTION_POOL_SIZE, DISABLE_TRUST_MANAGER, PROXY_URL, TRUSTSTORE, TRUSTSTORE_PASSWORD}; + CONNECTION_POOL_SIZE, DISABLE_TRUST_MANAGER, PROXY_URL, TRUSTSTORE, TRUSTSTORE_PASSWORD, SOCKET_TIMEOUT, CONNECTION_TIMEOUT}; private static final HashMap ATTRIBUTE_MAP = new HashMap<>(); diff --git a/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java b/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java index 7b3631a9bdd..1782731a204 100755 --- a/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java +++ b/adapters/saml/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java @@ -39,8 +39,9 @@ public class KeycloakSamlExtension implements Extension { private static final String NAMESPACE_1_1 = "urn:jboss:domain:keycloak-saml:1.1"; private static final String NAMESPACE_1_2 = "urn:jboss:domain:keycloak-saml:1.2"; private static final String NAMESPACE_1_3 = "urn:jboss:domain:keycloak-saml:1.3"; + private static final String NAMESPACE_1_4 = "urn:jboss:domain:keycloak-saml:1.4"; - static final String CURRENT_NAMESPACE = NAMESPACE_1_3; + static final String CURRENT_NAMESPACE = NAMESPACE_1_4; private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser(); static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions"; @@ -63,6 +64,7 @@ public class KeycloakSamlExtension implements Extension { context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE_1_1, PARSER); context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE_1_2, PARSER); context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE_1_3, PARSER); + context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE_1_4, PARSER); } /** diff --git a/adapters/saml/as7-eap6/subsystem/src/main/resources/org/keycloak/subsystem/saml/as7/LocalDescriptions.properties b/adapters/saml/as7-eap6/subsystem/src/main/resources/org/keycloak/subsystem/saml/as7/LocalDescriptions.properties index d3329ecfc02..0d3fc07a69e 100755 --- a/adapters/saml/as7-eap6/subsystem/src/main/resources/org/keycloak/subsystem/saml/as7/LocalDescriptions.properties +++ b/adapters/saml/as7-eap6/subsystem/src/main/resources/org/keycloak/subsystem/saml/as7/LocalDescriptions.properties @@ -99,4 +99,6 @@ keycloak-saml.IDP.HttpClient.connectionPoolSize=The number of pooled connections keycloak-saml.IDP.HttpClient.disableTrustManager=Define if SSL certificate validation should be disabled (true) or not (false) keycloak-saml.IDP.HttpClient.proxyUrl=URL to the HTTP proxy, if applicable keycloak-saml.IDP.HttpClient.truststore=Path to the truststore used to validate the IDP certificates -keycloak-saml.IDP.HttpClient.truststorePassword=The truststore password \ No newline at end of file +keycloak-saml.IDP.HttpClient.truststorePassword=The truststore password +keycloak-saml.IDP.HttpClient.socketTimeout=Timeout for socket waiting for data +keycloak-saml.IDP.HttpClient.connectionTimeout=Timeout for establishing the connection with the remote host \ No newline at end of file diff --git a/adapters/saml/as7-eap6/subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_4.xsd b/adapters/saml/as7-eap6/subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_4.xsd new file mode 100644 index 00000000000..decb9392d5a --- /dev/null +++ b/adapters/saml/as7-eap6/subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_4.xsd @@ -0,0 +1,580 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + The name of the deployment + + + + + + + + + + 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. + + + + + Specifies the role mappings provider implementation that will be used to map the roles extracted from the SAML assertion into the final set of roles + that will be assigned to the principal. A provider is typically used to map roles retrieved from third party IDPs into roles that exist in the JEE application environment. It can also + assign extra roles to the assertion principal (for example, by connecting to an LDAP server to obtain more roles) or remove some of the roles that were set by the IDP. + + + + + 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. + + + + + Attribute to inject the DOM representation of the assertion into the SamlPrincipal (respecting the original syntax). 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. + + + + + This should be set to true if your application serves both a web application and web services (e.g. SOAP or REST). It allows you to redirect unauthenticated users of the web application to the Keycloak login page, but send an HTTP 401 status code to unauthenticated SOAP or REST clients instead as they would not understand a redirect to the login page. Keycloak auto-detects SOAP or REST clients based on typical headers like X-Requested-With, SOAPAction or Accept. The 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. + + + + + Key store format + + + + + Key alias + + + + + + + + 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. + + + + + + + + + Specifies a configuration property for the provider. + + + + + + The id of the role mappings provider that is to be used. Example: properties-based-provider. + + + + + + + + The name (key) of the configuration property. + + + + + The value of the configuration property. + + + + + + + + + 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. + + + + + This defines the allowed clock skew between IDP and SP in milliseconds. The default value is 0. + + + + + + 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 https://www.w3.org/2001/10/xml-exc-c14n# and should be good for most IDPs. + + + + + + + + + + The URL used to retrieve the IDP metadata, currently this is only used to pick up signing and encryption keys periodically which allow cycling of these keys on the IDP without manual changes on the SP side. + + + + + + + + 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. This value maps to ProtocolBinding attribute in SAML AuthnRequest. 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. + + + + + URL of the assertion consumer service (ACS) where the IDP login service should send responses to. By default it is unset, relying on the IdP settings. When set, it must end in "/saml". This property is typically accompanied by the responseBinding attribute. + + + + + + + + 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. + + + + + Defines timeout for socket waiting for data in milliseconds. + + + + + Defines timeout for establishing the connection with the remote host in milliseconds. + + + + + + + The value is the allowed clock skew between the IDP and the SP. + + + + + + + + + + Time unit for the value of the clock skew. + + + + + + + + + + + diff --git a/adapters/saml/as7-eap6/subsystem/src/test/java/org/keycloak/subsystem/saml/as7/SubsystemParsingAllowedClockSkewTestCase.java b/adapters/saml/as7-eap6/subsystem/src/test/java/org/keycloak/subsystem/saml/as7/SubsystemParsingAllowedClockSkewTestCase.java index 72ed0d6f7c4..4718b535670 100755 --- a/adapters/saml/as7-eap6/subsystem/src/test/java/org/keycloak/subsystem/saml/as7/SubsystemParsingAllowedClockSkewTestCase.java +++ b/adapters/saml/as7-eap6/subsystem/src/test/java/org/keycloak/subsystem/saml/as7/SubsystemParsingAllowedClockSkewTestCase.java @@ -76,7 +76,7 @@ public class SubsystemParsingAllowedClockSkewTestCase extends AbstractSubsystemB private void setSubsystemXml(String value, String unit) throws IOException { try { - String template = readResource("keycloak-saml-1.3.xml"); + String template = readResource("keycloak-saml-1.4.xml"); if (value != null) { // assign the AllowedClockSkew element using DOM DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); diff --git a/adapters/saml/as7-eap6/subsystem/src/test/java/org/keycloak/subsystem/saml/as7/SubsystemParsingTestCase.java b/adapters/saml/as7-eap6/subsystem/src/test/java/org/keycloak/subsystem/saml/as7/SubsystemParsingTestCase.java index 502c45c7eaf..37172c553a2 100755 --- a/adapters/saml/as7-eap6/subsystem/src/test/java/org/keycloak/subsystem/saml/as7/SubsystemParsingTestCase.java +++ b/adapters/saml/as7-eap6/subsystem/src/test/java/org/keycloak/subsystem/saml/as7/SubsystemParsingTestCase.java @@ -74,7 +74,7 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest { @Before public void initialize() throws IOException { - this.subsystemTemplate = readResource("keycloak-saml-1.3.xml"); + this.subsystemTemplate = readResource("keycloak-saml-1.4.xml"); try { DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); this.document = builder.parse(new InputSource(new StringReader(this.subsystemTemplate))); diff --git a/adapters/saml/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.3.xml b/adapters/saml/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.4.xml similarity index 87% rename from adapters/saml/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.3.xml rename to adapters/saml/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.4.xml index 9a34e726fed..acb01a23efd 100755 --- a/adapters/saml/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.3.xml +++ b/adapters/saml/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.4.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - + + clientKeystore="/tmp/keystore.jks" + clientKeystorePassword="testpwd1!@" + connectionPoolSize="20" + disableTrustManager="false" + proxyUrl="http://localhost:9090/proxy" + truststore="/tmp/truststore.jks" + truststorePassword="trustpwd#*" + socketTimeout="6000" + /> 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 index 5c94fdb964f..3148579cbbd 100644 --- 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 @@ -29,29 +29,30 @@ public interface AdapterHttpClientConfig { /** * Returns truststore filename. */ - public String getTruststore(); + String getTruststore(); /** * Returns truststore password. */ - public String getTruststorePassword(); + String getTruststorePassword(); /** * Returns keystore with client keys. */ - public String getClientKeystore(); + String getClientKeystore(); /** * Returns keystore password. */ - public String getClientKeystorePassword(); + 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(); + boolean isAllowAnyHostname(); /** * Returns boolean flag whether any trust management and hostname verification is done. @@ -60,16 +61,26 @@ public interface AdapterHttpClientConfig { * if you cannot or do not want to verify the identity of the * host you are communicating with. */ - public boolean isDisableTrustManager(); + boolean isDisableTrustManager(); /** * Returns size of connection pool. */ - public int getConnectionPoolSize(); + int getConnectionPoolSize(); /** * Returns URL of HTTP proxy. */ - public String getProxyUrl(); + String getProxyUrl(); + + /** + * Returns timeout for socket waiting for data in milliseconds. + */ + long getSocketTimeout(); + + /** + * Returns timeout for establishing the connection with the remote host in milliseconds. + */ + long getConnectionTimeout(); } 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 index 5a8c947eeb4..28ea52515ba 100644 --- 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 @@ -373,6 +373,14 @@ public class HttpClientBuilder { trustStore(truststore); } + if (socketTimeout == -1 && adapterConfig.getSocketTimeout() > 0) { + socketTimeout(adapterConfig.getSocketTimeout(), TimeUnit.MILLISECONDS); + } + + if (establishConnectionTimeout == -1 && adapterConfig.getConnectionTimeout() > 0) { + establishConnectionTimeout(adapterConfig.getConnectionTimeout(), TimeUnit.MILLISECONDS); + } + configureProxyForAuthServerIfProvided(adapterConfig); return build(); 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 2ffde0b8827..9640968fdfd 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 @@ -189,6 +189,8 @@ public class IDP implements Serializable { private boolean disableTrustManager; private int connectionPoolSize; private String proxyUrl; + private long socketTimeout; + private long connectionTimeout; @Override public String getTruststore() { @@ -258,6 +260,24 @@ public class IDP implements Serializable { return proxyUrl; } + @Override + public long getSocketTimeout() { + return socketTimeout; + } + + public void setSocketTimeout(long socketTimeout) { + this.socketTimeout = socketTimeout; + } + + @Override + public long getConnectionTimeout() { + return connectionTimeout; + } + + public void setConnectionTimeout(long connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } + public void setProxyUrl(String proxyUrl) { this.proxyUrl = proxyUrl; } diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/HttpClientParser.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/HttpClientParser.java index b365fd74940..5ca23db8ed0 100644 --- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/HttpClientParser.java +++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/config/parsers/HttpClientParser.java @@ -55,6 +55,10 @@ public class HttpClientParser extends AbstractKeycloakSamlAdapterV1Parser + + + + + + + + + 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. + + + + + Specifies the role mappings provider implementation that will be used to map the roles extracted from the SAML assertion into the final set of roles + that will be assigned to the principal. A provider is typically used to map roles retrieved from third party IDPs into roles that exist in the JEE application environment. It can also + assign extra roles to the assertion principal (for example, by connecting to an LDAP server to obtain more roles) or remove some of the roles that were set by the IDP. + + + + + 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. + + + + + Attribute to inject the DOM representation of the assertion into the SamlPrincipal (respecting the original syntax). 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. + + + + + This should be set to true if your application serves both a web application and web services (e.g. SOAP or REST). It allows you to redirect unauthenticated users of the web application to the Keycloak login page, but send an HTTP 401 status code to unauthenticated SOAP or REST clients instead as they would not understand a redirect to the login page. Keycloak auto-detects SOAP or REST clients based on typical headers like X-Requested-With, SOAPAction or Accept. The 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. + + + + + Key store format + + + + + Key alias + + + + + + + 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. + + + + + + + + Specifies a configuration property for the provider. + + + + + + The id of the role mappings provider that is to be used. Example: properties-based-provider. + + + + + + + The name (key) of the configuration property. + + + + + The value of the configuration property. + + + + + + + + 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. + + + + + This defines the allowed clock skew between IDP and SP in milliseconds. The default value is 0. + + + + + + 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 https://www.w3.org/2001/10/xml-exc-c14n# and should be good for most IDPs. + + + + + + + + + + The URL used to retrieve the IDP metadata, currently this is only used to pick up signing and encryption keys periodically which allow cycling of these keys on the IDP without manual changes on the SP side. + + + + + + + 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. This value maps to ProtocolBinding attribute in SAML AuthnRequest. 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. + + + + + URL of the assertion consumer service (ACS) where the IDP login service should send responses to. By default it is unset, relying on the IdP settings. When set, it must end in "/saml". This property is typically accompanied by the responseBinding attribute. + + + + + + + + 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. + + + + + Defines timeout for socket waiting for data in milliseconds. + + + + + Defines timeout for establishing the connection with the remote host in milliseconds. + + + + + + The value is the allowed clock skew between the IDP and the SP. + + + + + + + + + + Time unit for the value of the clock skew. + + + + + + + + + + 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 index e5115f65b2a..b957c8dcd8b 100755 --- 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 @@ -17,8 +17,9 @@ package org.keycloak.adapters.saml.config.parsers; -import static org.junit.Assert.*; import static org.hamcrest.CoreMatchers.*; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; import org.junit.Test; import org.keycloak.adapters.saml.config.IDP; @@ -43,7 +44,7 @@ import org.hamcrest.Matchers; */ public class KeycloakSamlAdapterXMLParserTest { - private static final String CURRENT_XSD_LOCATION = "/schema/keycloak_saml_adapter_1_12.xsd"; + private static final String CURRENT_XSD_LOCATION = "/schema/keycloak_saml_adapter_1_13.xsd"; @Rule public ExpectedException expectedException = ExpectedException.none(); @@ -51,8 +52,8 @@ public class KeycloakSamlAdapterXMLParserTest { private void testValidationValid(String fileName) throws Exception { InputStream schema = getClass().getResourceAsStream(CURRENT_XSD_LOCATION); InputStream is = getClass().getResourceAsStream(fileName); - assertNotNull(is); - assertNotNull(schema); + assertThat(is, Matchers.notNullValue()); + assertThat(schema, Matchers.notNullValue()); StaxParserUtil.validate(is, schema); } @@ -91,18 +92,18 @@ public class KeycloakSamlAdapterXMLParserTest { testValidationValid("keycloak-saml-keepdomassertion.xml"); // check keep dom assertion is TRUE KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-keepdomassertion.xml", KeycloakSamlAdapter.class); - assertNotNull(config); - assertEquals(1, config.getSps().size()); + assertThat(config, Matchers.notNullValue()); + assertThat(config.getSps(), hasSize(1)); SP sp = config.getSps().get(0); - assertTrue(sp.isKeepDOMAssertion()); + assertThat(sp.isKeepDOMAssertion(), is(true)); } @Test public void testValidationKeyInvalid() throws Exception { InputStream schemaIs = KeycloakSamlAdapterV1Parser.class.getResourceAsStream(CURRENT_XSD_LOCATION); InputStream is = getClass().getResourceAsStream("keycloak-saml-invalid.xml"); - assertNotNull(is); - assertNotNull(schemaIs); + assertThat(is, Matchers.notNullValue()); + assertThat(schemaIs, Matchers.notNullValue()); expectedException.expect(ParsingException.class); StaxParserUtil.validate(is, schemaIs); @@ -117,57 +118,59 @@ public class KeycloakSamlAdapterXMLParserTest { public void testXmlParserBaseFile() throws Exception { KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml.xml", KeycloakSamlAdapter.class); - assertNotNull(config); - assertEquals(1, config.getSps().size()); + assertThat(config, notNullValue()); + assertThat(config.getSps(), hasSize(1)); + SP sp = config.getSps().get(0); - assertEquals("sp", sp.getEntityID()); - assertEquals("EXTERNAL", sp.getSslPolicy()); - assertEquals("format", sp.getNameIDPolicyFormat()); - assertTrue(sp.isForceAuthentication()); - assertTrue(sp.isIsPassive()); - assertFalse(sp.isAutodetectBearerOnly()); - assertFalse(sp.isKeepDOMAssertion()); - assertEquals(2, sp.getKeys().size()); + assertThat(sp.getEntityID(), is("sp")); + assertThat(sp.getSslPolicy(), is("EXTERNAL")); + assertThat(sp.getNameIDPolicyFormat(), is("format")); + assertThat(sp.isForceAuthentication(), is(true)); + assertThat(sp.isIsPassive(), is(true)); + assertThat(sp.isAutodetectBearerOnly(), is(false)); + assertThat(sp.isKeepDOMAssertion(), is(false)); + assertThat(sp.getKeys(), hasSize(2)); + Key signing = sp.getKeys().get(0); - assertTrue(signing.isSigning()); + assertThat(signing.isSigning(), is(true)); 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()); + assertThat(keystore, notNullValue()); + assertThat(keystore.getFile(), is("file")); + assertThat(keystore.getResource(), is("cp")); + assertThat(keystore.getPassword(), is("pw")); + assertThat(keystore.getPrivateKeyAlias(), is("private alias")); + assertThat(keystore.getPrivateKeyPassword(), is("private pw")); + assertThat(keystore.getCertificateAlias(), is("cert alias")); 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")); + assertThat(encryption.isEncryption(), is(true)); + assertThat(encryption.getPrivateKeyPem(), is("private pem")); + assertThat(encryption.getPublicKeyPem(), is("public pem")); + assertThat(sp.getPrincipalNameMapping().getPolicy(), is("FROM_ATTRIBUTE")); + assertThat(sp.getPrincipalNameMapping().getAttributeName(), is("attribute")); + assertThat(sp.getRoleAttributes(), hasSize(1)); + assertThat(sp.getRoleAttributes(), Matchers.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()); + assertThat(idp.getEntityID(), is("idp")); + assertThat(idp.getSignatureAlgorithm(), is("RSA_SHA256")); + assertThat(idp.getSignatureCanonicalizationMethod(), is("canon")); + assertThat(idp.getSingleSignOnService().isSignRequest(), is(true)); + assertThat(idp.getSingleSignOnService().isValidateResponseSignature(), is(true)); + assertThat(idp.getSingleSignOnService().getRequestBinding(), is("POST")); + assertThat(idp.getSingleSignOnService().getBindingUrl(), is("url")); - assertFalse(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()); + assertThat(idp.getSingleLogoutService().isSignRequest(), is(false)); + assertThat(idp.getSingleLogoutService().isSignResponse(), is(true)); + assertThat(idp.getSingleLogoutService().isValidateRequestSignature(), is(true)); + assertThat(idp.getSingleLogoutService().isValidateResponseSignature(), is(true)); + assertThat(idp.getSingleLogoutService().getRequestBinding(), is("REDIRECT")); + assertThat(idp.getSingleLogoutService().getResponseBinding(), is("POST")); + assertThat(idp.getSingleLogoutService().getPostBindingUrl(), is("posturl")); + assertThat(idp.getSingleLogoutService().getRedirectBindingUrl(), is("redirecturl")); - assertTrue(idp.getKeys().size() == 1); - assertTrue(idp.getKeys().get(0).isSigning()); - assertEquals("cert pem", idp.getKeys().get(0).getCertificatePem()); + assertThat(idp.getKeys(), hasSize(1)); + assertThat(idp.getKeys().get(0).isSigning(), is(true)); + assertThat(idp.getKeys().get(0).getCertificatePem(), is("cert pem")); } private T parseKeycloakSamlAdapterConfig(String fileName, Class targetClass) throws ParsingException, IOException { @@ -181,24 +184,24 @@ public class KeycloakSamlAdapterXMLParserTest { @Test public void testXmlParserMultipleSigningKeys() throws Exception { KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-multiple-signing-keys.xml", KeycloakSamlAdapter.class); - assertNotNull(config); - assertEquals(1, config.getSps().size()); + assertThat(config, notNullValue()); + assertThat(config.getSps(), hasSize(1)); SP sp = config.getSps().get(0); IDP idp = sp.getIdp(); - assertTrue(idp.getKeys().size() == 4); - for (int i = 0; i < 4; i ++) { + assertThat(idp.getKeys(), hasSize(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()); + assertThat(key.isSigning(), is(true)); + assertThat(idp.getKeys().get(i).getCertificatePem(), is("cert pem " + i)); } } @Test public void testXmlParserHttpClientSettings() throws Exception { KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-wth-http-client-settings.xml", KeycloakSamlAdapter.class); - assertNotNull(config); - assertEquals(1, config.getSps().size()); + assertThat(config, notNullValue()); + assertThat(config.getSps(), hasSize(1)); SP sp = config.getSps().get(0); IDP idp = sp.getIdp(); @@ -211,12 +214,14 @@ public class KeycloakSamlAdapterXMLParserTest { assertThat(idp.getHttpClientConfig().getConnectionPoolSize(), is(42)); assertThat(idp.getHttpClientConfig().isAllowAnyHostname(), is(true)); assertThat(idp.getHttpClientConfig().isDisableTrustManager(), is(true)); + assertThat(idp.getHttpClientConfig().getSocketTimeout(), is(6000L)); + assertThat(idp.getHttpClientConfig().getConnectionTimeout(), is(7000L)); } @Test public void testXmlParserSystemPropertiesNoPropertiesSet() throws Exception { KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-properties.xml", KeycloakSamlAdapter.class); - assertNotNull(config); + assertThat(config, notNullValue()); assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class))); SP sp = config.getSps().get(0); IDP idp = sp.getIdp(); @@ -247,7 +252,7 @@ public class KeycloakSamlAdapterXMLParserTest { System.setProperty("keycloak-saml-properties.signaturesRequired", "true"); KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-properties.xml", KeycloakSamlAdapter.class); - assertNotNull(config); + assertThat(config, notNullValue()); assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class))); SP sp = config.getSps().get(0); IDP idp = sp.getIdp(); @@ -278,7 +283,7 @@ public class KeycloakSamlAdapterXMLParserTest { @Test public void testMetadataUrl() throws Exception { KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-with-metadata-url.xml", KeycloakSamlAdapter.class); - assertNotNull(config); + assertThat(config, notNullValue()); assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class))); SP sp = config.getSps().get(0); IDP idp = sp.getIdp(); @@ -288,7 +293,7 @@ public class KeycloakSamlAdapterXMLParserTest { @Test public void testAllowedClockSkewDefaultUnit() throws Exception { KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-with-allowed-clock-skew-default-unit.xml", KeycloakSamlAdapter.class); - assertNotNull(config); + assertThat(config, notNullValue()); assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class))); SP sp = config.getSps().get(0); IDP idp = sp.getIdp(); @@ -299,7 +304,7 @@ public class KeycloakSamlAdapterXMLParserTest { @Test public void testAllowedClockSkewWithUnit() throws Exception { KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-with-allowed-clock-skew-with-unit.xml", KeycloakSamlAdapter.class); - assertNotNull(config); + assertThat(config, notNullValue()); assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class))); SP sp = config.getSps().get(0); IDP idp = sp.getIdp(); @@ -310,17 +315,17 @@ public class KeycloakSamlAdapterXMLParserTest { @Test public void testParseRoleMappingsProvider() throws Exception { KeycloakSamlAdapter config = parseKeycloakSamlAdapterConfig("keycloak-saml-with-role-mappings-provider.xml", KeycloakSamlAdapter.class); - assertNotNull(config); + assertThat(config, notNullValue()); assertThat(config.getSps(), Matchers.contains(instanceOf(SP.class))); SP sp = config.getSps().get(0); SP.RoleMappingsProviderConfig roleMapperConfig = sp.getRoleMappingsProviderConfig(); - assertNotNull(roleMapperConfig); + assertThat(roleMapperConfig, notNullValue()); assertThat(roleMapperConfig.getId(), is("properties-based-role-mapper")); Properties providerConfig = roleMapperConfig.getConfiguration(); assertThat(providerConfig.size(), is(2)); - assertTrue(providerConfig.containsKey("properties.resource.location")); - assertEquals("role-mappings.properties", providerConfig.getProperty("properties.resource.location")); - assertTrue(providerConfig.containsKey("another.property")); - assertEquals("another.value", providerConfig.getProperty("another.property")); + assertThat(providerConfig.containsKey("properties.resource.location"), is(true)); + assertThat(providerConfig.getProperty("properties.resource.location"), is("role-mappings.properties")); + assertThat(providerConfig.containsKey("another.property"), is(true)); + assertThat(providerConfig.getProperty("another.property"), is("another.value")); } } 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 index 0c4abb21d75..a958f7abad7 100644 --- 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 @@ -17,7 +17,7 @@ + xsi:schemaLocation="urn:keycloak:saml:adapter http://www.keycloak.org/schema/keycloak_saml_adapter_1_13.xsd"> diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Constants.java b/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Constants.java index 9d22fd9a80b..fb44dd5f992 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Constants.java +++ b/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Constants.java @@ -93,6 +93,8 @@ public class Constants { static final String PROXY_URL = "proxyUrl"; static final String TRUSTSTORE = "truststore"; static final String TRUSTSTORE_PASSWORD = "truststorePassword"; + static final String SOCKET_TIMEOUT = "socketTimeout"; + static final String CONNECTION_TIMEOUT = "connectionTimeout"; } static class XML { @@ -172,6 +174,8 @@ public class Constants { static final String PROXY_URL = "proxyUrl"; static final String TRUSTSTORE = "truststore"; static final String TRUSTSTORE_PASSWORD = "truststorePassword"; + static final String SOCKET_TIMEOUT = "socketTimeout"; + static final String CONNECTION_TIMEOUT = "connectionTimeout"; } } diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/HttpClientDefinition.java b/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/HttpClientDefinition.java index db83b73d9c3..a3adc6839ca 100644 --- a/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/HttpClientDefinition.java +++ b/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/HttpClientDefinition.java @@ -78,8 +78,20 @@ abstract class HttpClientDefinition { .setAllowExpression(true) .build(); + private static final SimpleAttributeDefinition SOCKET_TIMEOUT = + new SimpleAttributeDefinitionBuilder(Constants.Model.SOCKET_TIMEOUT, ModelType.LONG, true) + .setXmlName(Constants.XML.SOCKET_TIMEOUT) + .setAllowExpression(true) + .build(); + + private static final SimpleAttributeDefinition CONNECTION_TIMEOUT = + new SimpleAttributeDefinitionBuilder(Constants.Model.CONNECTION_TIMEOUT, ModelType.LONG, true) + .setXmlName(Constants.XML.CONNECTION_TIMEOUT) + .setAllowExpression(true) + .build(); + static final SimpleAttributeDefinition[] ATTRIBUTES = {ALLOW_ANY_HOSTNAME, CLIENT_KEYSTORE, CLIENT_KEYSTORE_PASSWORD, - CONNECTION_POOL_SIZE, DISABLE_TRUST_MANAGER, PROXY_URL, TRUSTSTORE, TRUSTSTORE_PASSWORD}; + CONNECTION_POOL_SIZE, DISABLE_TRUST_MANAGER, PROXY_URL, TRUSTSTORE, TRUSTSTORE_PASSWORD, SOCKET_TIMEOUT, CONNECTION_TIMEOUT}; private static final HashMap ATTRIBUTE_MAP = new HashMap<>(); diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java b/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java index ef70a945ec2..b2a2a4b8d47 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java +++ b/adapters/saml/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java @@ -39,8 +39,9 @@ public class KeycloakSamlExtension implements Extension { private static final String NAMESPACE_1_1 = "urn:jboss:domain:keycloak-saml:1.1"; private static final String NAMESPACE_1_2 = "urn:jboss:domain:keycloak-saml:1.2"; private static final String NAMESPACE_1_3 = "urn:jboss:domain:keycloak-saml:1.3"; + private static final String NAMESPACE_1_4 = "urn:jboss:domain:keycloak-saml:1.4"; - static final String CURRENT_NAMESPACE = NAMESPACE_1_3; + static final String CURRENT_NAMESPACE = NAMESPACE_1_4; private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser(); static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions"; @@ -63,6 +64,7 @@ public class KeycloakSamlExtension implements Extension { context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE_1_1, PARSER); context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE_1_2, PARSER); context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE_1_3, PARSER); + context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE_1_4, PARSER); } /** diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/saml/extension/LocalDescriptions.properties b/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/saml/extension/LocalDescriptions.properties index 0ad519f9423..24b1bded9a3 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/saml/extension/LocalDescriptions.properties +++ b/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/saml/extension/LocalDescriptions.properties @@ -98,4 +98,6 @@ keycloak-saml.IDP.HttpClient.connectionPoolSize=The number of pooled connections keycloak-saml.IDP.HttpClient.disableTrustManager=Define if SSL certificate validation should be disabled (true) or not (false) keycloak-saml.IDP.HttpClient.proxyUrl=URL to the HTTP proxy, if applicable keycloak-saml.IDP.HttpClient.truststore=Path to the truststore used to validate the IDP certificates -keycloak-saml.IDP.HttpClient.truststorePassword=The truststore password \ No newline at end of file +keycloak-saml.IDP.HttpClient.truststorePassword=The truststore password +keycloak-saml.IDP.HttpClient.socketTimeout=Timeout for socket waiting for data in milliseconds +keycloak-saml.IDP.HttpClient.connectionTimeout=Timeout for establishing the connection with the remote host in milliseconds \ No newline at end of file diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_4.xsd b/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_4.xsd new file mode 100644 index 00000000000..decb9392d5a --- /dev/null +++ b/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_4.xsd @@ -0,0 +1,580 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + The name of the deployment + + + + + + + + + + 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. + + + + + Specifies the role mappings provider implementation that will be used to map the roles extracted from the SAML assertion into the final set of roles + that will be assigned to the principal. A provider is typically used to map roles retrieved from third party IDPs into roles that exist in the JEE application environment. It can also + assign extra roles to the assertion principal (for example, by connecting to an LDAP server to obtain more roles) or remove some of the roles that were set by the IDP. + + + + + 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. + + + + + Attribute to inject the DOM representation of the assertion into the SamlPrincipal (respecting the original syntax). 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. + + + + + This should be set to true if your application serves both a web application and web services (e.g. SOAP or REST). It allows you to redirect unauthenticated users of the web application to the Keycloak login page, but send an HTTP 401 status code to unauthenticated SOAP or REST clients instead as they would not understand a redirect to the login page. Keycloak auto-detects SOAP or REST clients based on typical headers like X-Requested-With, SOAPAction or Accept. The 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. + + + + + Key store format + + + + + Key alias + + + + + + + + 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. + + + + + + + + + Specifies a configuration property for the provider. + + + + + + The id of the role mappings provider that is to be used. Example: properties-based-provider. + + + + + + + + The name (key) of the configuration property. + + + + + The value of the configuration property. + + + + + + + + + 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. + + + + + This defines the allowed clock skew between IDP and SP in milliseconds. The default value is 0. + + + + + + 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 https://www.w3.org/2001/10/xml-exc-c14n# and should be good for most IDPs. + + + + + + + + + + The URL used to retrieve the IDP metadata, currently this is only used to pick up signing and encryption keys periodically which allow cycling of these keys on the IDP without manual changes on the SP side. + + + + + + + + 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. This value maps to ProtocolBinding attribute in SAML AuthnRequest. 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. + + + + + URL of the assertion consumer service (ACS) where the IDP login service should send responses to. By default it is unset, relying on the IdP settings. When set, it must end in "/saml". This property is typically accompanied by the responseBinding attribute. + + + + + + + + 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. + + + + + Defines timeout for socket waiting for data in milliseconds. + + + + + Defines timeout for establishing the connection with the remote host in milliseconds. + + + + + + + The value is the allowed clock skew between the IDP and the SP. + + + + + + + + + + Time unit for the value of the clock skew. + + + + + + + + + + + diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak-saml-adapter.xml b/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak-saml-adapter.xml index ef4534b8f1f..9a323e2b6b5 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak-saml-adapter.xml +++ b/adapters/saml/wildfly/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak-saml-adapter.xml @@ -19,6 +19,6 @@ org.keycloak.keycloak-saml-adapter-subsystem - + diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingAllowedClockSkewTestCase.java b/adapters/saml/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingAllowedClockSkewTestCase.java index b3ccf4de7b5..22144881ec4 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingAllowedClockSkewTestCase.java +++ b/adapters/saml/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingAllowedClockSkewTestCase.java @@ -77,7 +77,7 @@ public class SubsystemParsingAllowedClockSkewTestCase extends AbstractSubsystemB @Override protected String getSubsystemXsdPath() throws Exception { - return "schema/wildfly-keycloak-saml_1_3.xsd"; + return "schema/wildfly-keycloak-saml_1_4.xsd"; } @Override @@ -94,7 +94,7 @@ public class SubsystemParsingAllowedClockSkewTestCase extends AbstractSubsystemB private void setSubsystemXml(String value, String unit) throws IOException { try { - String template = readResource("keycloak-saml-1.3.xml"); + String template = readResource("keycloak-saml-1.4.xml"); if (value != null) { // assign the AllowedClockSkew element using DOM DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingTestCase.java b/adapters/saml/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingTestCase.java index 433b16d5928..ff8089f46a2 100755 --- a/adapters/saml/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingTestCase.java +++ b/adapters/saml/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingTestCase.java @@ -79,7 +79,7 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest { @Override protected String getSubsystemXsdPath() throws Exception { - return "schema/wildfly-keycloak-saml_1_3.xsd"; + return "schema/wildfly-keycloak-saml_1_4.xsd"; } @Override @@ -91,7 +91,7 @@ public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest { @Before public void initialize() throws IOException { - this.subsystemTemplate = readResource("keycloak-saml-1.3.xml"); + this.subsystemTemplate = readResource("keycloak-saml-1.4.xml"); try { DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); this.document = builder.parse(new InputSource(new StringReader(this.subsystemTemplate))); diff --git a/adapters/saml/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.3.xml b/adapters/saml/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.4.xml old mode 100755 new mode 100644 similarity index 87% rename from adapters/saml/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.3.xml rename to adapters/saml/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.4.xml index 9a34e726fed..acb01a23efd --- a/adapters/saml/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.3.xml +++ b/adapters/saml/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.4.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - + + clientKeystore="/tmp/keystore.jks" + clientKeystorePassword="testpwd1!@" + connectionPoolSize="20" + disableTrustManager="false" + proxyUrl="http://localhost:9090/proxy" + truststore="/tmp/truststore.jks" + truststorePassword="trustpwd#*" + socketTimeout="6000" + /> 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 555cde28f89..bebf7f6ba82 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 @@ -90,6 +90,11 @@ public class AdapterConfig extends BaseAdapterConfig implements AdapterHttpClien @JsonProperty("verify-token-audience") protected boolean verifyTokenAudience = false; + @JsonProperty("socket-timeout") + protected long socketTimeout = -1; + @JsonProperty("connection-timeout") + protected long connectionTimeout = -1; + /** * The Proxy url to use for requests to the auth-server, configurable via the adapter config property {@code proxy-url}. */ @@ -288,4 +293,20 @@ public class AdapterConfig extends BaseAdapterConfig implements AdapterHttpClien public void setVerifyTokenAudience(boolean verifyTokenAudience) { this.verifyTokenAudience = verifyTokenAudience; } + + public long getSocketTimeout() { + return socketTimeout; + } + + public void setSocketTimeout(long socketTimeout) { + this.socketTimeout = socketTimeout; + } + + public long getConnectionTimeout() { + return connectionTimeout; + } + + public void setConnectionTimeout(long connectionTimeout) { + this.connectionTimeout = connectionTimeout; + } } 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 index 1875f036dea..90606eb6261 100644 --- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java +++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java @@ -72,4 +72,13 @@ public interface AdapterHttpClientConfig { */ String getProxyUrl(); + /** + * Returns timeout for socket waiting for data in milliseconds. + */ + long getSocketTimeout(); + + /** + * Returns timeout for establishing the connection with the remote host in milliseconds. + */ + long getConnectionTimeout(); } diff --git a/core/src/test/java/org/keycloak/JsonParserTest.java b/core/src/test/java/org/keycloak/JsonParserTest.java index d61fe9f29f9..26f464e757f 100755 --- a/core/src/test/java/org/keycloak/JsonParserTest.java +++ b/core/src/test/java/org/keycloak/JsonParserTest.java @@ -100,6 +100,8 @@ public class JsonParserTest { System.setProperty("my.host", "foo"); System.setProperty("con.pool.size", "200"); System.setProperty("allow.any.hostname", "true"); + System.setProperty("socket.timeout.millis", "6000"); + System.setProperty("connection.timeout.millis", "7000"); InputStream is = getClass().getClassLoader().getResourceAsStream("keycloak.json"); @@ -111,6 +113,8 @@ public class JsonParserTest { Assert.assertTrue(config.isAllowAnyHostname()); Assert.assertEquals(100, config.getCorsMaxAge()); Assert.assertEquals(200, config.getConnectionPoolSize()); + Assert.assertEquals(6000L, config.getSocketTimeout()); + Assert.assertEquals(7000L, config.getConnectionTimeout()); } static Pattern substitution = Pattern.compile("\\$\\{([^}]+)\\}"); diff --git a/core/src/test/resources/keycloak.json b/core/src/test/resources/keycloak.json index b0a893505c8..8289f0d8bbd 100644 --- a/core/src/test/resources/keycloak.json +++ b/core/src/test/resources/keycloak.json @@ -5,5 +5,7 @@ "public-client" : true, "allow-any-hostname": "${allow.any.hostname}", "cors-max-age": 100, - "connection-pool-size": "${con.pool.size}" + "connection-pool-size": "${con.pool.size}", + "socket-timeout": "${socket.timeout.millis}", + "connection-timeout": "${connection.timeout.millis}" } \ No newline at end of file diff --git a/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java b/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java index 10622bb012b..4c60a119971 100755 --- a/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java +++ b/saml-core/src/main/java/org/keycloak/saml/common/util/StaxParserUtil.java @@ -335,6 +335,30 @@ public class StaxParserUtil { return value == null ? null : Integer.valueOf(value); } + /** + * Get the Attribute value + * + * @param startElement + * @param attrName + */ + public static Long getLongAttributeValue(StartElement startElement, HasQName attrName) { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValue(attr); + return value == null ? null : Long.valueOf(value); + } + + /** + * Get the Attribute value + * + * @param startElement + * @param attrName + */ + public static Long getLongAttributeValueRP(StartElement startElement, HasQName attrName) { + Attribute attr = startElement.getAttributeByName(attrName.getQName()); + String value = getAttributeValueRP(attr); + return value == null ? null : Long.valueOf(value); + } + /** * Get the Attribute value *