From 3e9c729f9e32b3c50e19d5561e000d87b59522df Mon Sep 17 00:00:00 2001 From: mposolda Date: Mon, 21 Nov 2022 12:10:08 +0100 Subject: [PATCH] X.509 authentication fixes for FIPS Closes #14967 --- .github/workflows/ci.yml | 3 +- .../CertificateIdentityExtractorTest.java | 97 +++++++++++++++++++ .../src/test/resources/certs/UPN-cert.pem | 0 ...faultCertificateIdentityExtractorTest.java | 37 +++++++ ...S1402CertificateIdentityExtractorTest.java | 37 +++++++ docs/fips.md | 1 + .../client/X509ClientAuthenticator.java | 4 +- ...actX509ClientCertificateAuthenticator.java | 4 +- .../x509/CertificateValidator.java | 7 +- .../X509ClientCertificateAuthenticator.java | 6 +- .../CertificatePemIdentityExtractorTest.java | 36 ------- .../SubjectAltNameIdentityExtractorTest.java | 71 -------------- .../integration-arquillian/HOW-TO-RUN.md | 20 ++-- ...cloakQuarkusServerDeployableContainer.java | 26 +++-- .../keycloak/testsuite/client/FAPI1Test.java | 1 + .../testsuite/client/FAPICIBATest.java | 3 + .../testsuite/client/MutualTLSClientTest.java | 7 +- 17 files changed, 221 insertions(+), 139 deletions(-) create mode 100644 core/src/test/java/org/keycloak/authentication/x509/CertificateIdentityExtractorTest.java rename {services => core}/src/test/resources/certs/UPN-cert.pem (100%) create mode 100644 crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCertificateIdentityExtractorTest.java create mode 100644 crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402CertificateIdentityExtractorTest.java delete mode 100644 services/src/test/java/org/keycloak/authentication/authenticators/x509/CertificatePemIdentityExtractorTest.java delete mode 100644 services/src/test/java/org/keycloak/authentication/authenticators/x509/SubjectAltNameIdentityExtractorTest.java diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7e377f7d4e..c6e87ba336 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -299,7 +299,7 @@ jobs: strategy: matrix: server: ['bcfips-nonapproved-pkcs12'] - tests: ['group1'] + tests: ['group1', 'group2'] fail-fast: false steps: - uses: actions/checkout@v3 @@ -339,6 +339,7 @@ jobs: PARAMS["bcfips-nonapproved-pkcs12"]="-Pauth-server-quarkus,auth-server-fips140-2" # Tests in the package "forms" and some keystore related tests TESTGROUP["group1"]="-Dtest=org.keycloak.testsuite.forms.**,ClientAuthSignedJWTTest,CredentialsTest,JavaKeystoreKeyProviderTest,ServerInfoTest,UserFederationLdapConnectionTest,LDAPUserLoginTest" + TESTGROUP["group2"]="-Dtest=org.keycloak.testsuite.x509.**,MutualTLSClientTest,FAPI1Test,FAPICIBATest" # Tests for X.509 authentication with users and clients ./mvnw clean install -nsu -B ${PARAMS["${{ matrix.server }}"]} ${TESTGROUP["${{ matrix.tests }}"]} -f testsuite/integration-arquillian/tests/base/pom.xml | misc/log/trimmer.sh diff --git a/core/src/test/java/org/keycloak/authentication/x509/CertificateIdentityExtractorTest.java b/core/src/test/java/org/keycloak/authentication/x509/CertificateIdentityExtractorTest.java new file mode 100644 index 0000000000..44f9dba24a --- /dev/null +++ b/core/src/test/java/org/keycloak/authentication/x509/CertificateIdentityExtractorTest.java @@ -0,0 +1,97 @@ +/* + * Copyright 2022 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.authentication.x509; + +import java.io.InputStream; +import java.nio.charset.Charset; +import java.security.cert.X509Certificate; + +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; +import org.keycloak.common.crypto.CryptoIntegration; +import org.keycloak.common.crypto.UserIdentityExtractor; +import org.keycloak.common.util.PemUtils; +import org.keycloak.common.util.StreamUtil; +import org.keycloak.rule.CryptoInitRule; + +import static org.junit.Assert.assertEquals; + +/** This is not tested in keycloak-core. The subclasses should be created in the crypto modules to make sure it is tested with corresponding modules (bouncycastle VS bouncycastle-fips) */ +public abstract class CertificateIdentityExtractorTest { + + @ClassRule + public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); + + @Test + public void testExtractsCertInPemFormat() throws Exception { + X509Certificate x509Certificate = getCertificate(); + + String certificatePem = PemUtils.encodeCertificate(x509Certificate); + + //X509AuthenticatorConfigModel config = new X509AuthenticatorConfigModel(); + UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getCertificatePemIdentityExtractor(); + + String userIdentity = (String) extractor.extractUserIdentity(new X509Certificate[]{x509Certificate}); + + assertEquals(certificatePem, userIdentity); + } + + @Test + public void testExtractsCertInSubjectDNFormat() throws Exception { + X509Certificate x509Certificate = getCertificate(); + + UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getX500NameExtractor("CN", certs -> { + return certs[0].getSubjectX500Principal(); + }); + String userIdentity = (String) extractor.extractUserIdentity(new X509Certificate[]{x509Certificate}); + assertEquals("Test User", userIdentity); + } + + @Test + public void testX509SubjectAltName_otherName() throws Exception { + UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getSubjectAltNameExtractor(0); + + X509Certificate cert = getCertificate(); + + Object upn = extractor.extractUserIdentity(new X509Certificate[] { cert}); + Assert.assertEquals("test-user@some-company-domain", upn); + } + + + @Test + public void testX509SubjectAltName_email() throws Exception { + UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getSubjectAltNameExtractor(1); + + X509Certificate cert = getCertificate(); + + Object upn = extractor.extractUserIdentity(new X509Certificate[] { cert}); + Assert.assertEquals("test@somecompany.com", upn); + } + + + private X509Certificate getCertificate() throws Exception { + InputStream is = getClass().getResourceAsStream("/certs/UPN-cert.pem"); + + String s = StreamUtil.readString(is, Charset.defaultCharset()); + + return PemUtils.decodeCertificate(s); + } +} diff --git a/services/src/test/resources/certs/UPN-cert.pem b/core/src/test/resources/certs/UPN-cert.pem similarity index 100% rename from services/src/test/resources/certs/UPN-cert.pem rename to core/src/test/resources/certs/UPN-cert.pem diff --git a/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCertificateIdentityExtractorTest.java b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCertificateIdentityExtractorTest.java new file mode 100644 index 0000000000..1775092b8a --- /dev/null +++ b/crypto/default/src/test/java/org/keycloak/crypto/def/test/DefaultCertificateIdentityExtractorTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022 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.crypto.def.test; + +import org.junit.Assume; +import org.junit.Before; +import org.keycloak.authentication.x509.CertificateIdentityExtractorTest; +import org.keycloak.common.util.Environment; + +/** + * @author Marek Posolda + */ +public class DefaultCertificateIdentityExtractorTest extends CertificateIdentityExtractorTest { + + @Before + public void before() { + // Run this test just if java is not in FIPS mode + Assume.assumeFalse("Java is in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } +} diff --git a/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402CertificateIdentityExtractorTest.java b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402CertificateIdentityExtractorTest.java new file mode 100644 index 0000000000..bc1c12eb20 --- /dev/null +++ b/crypto/fips1402/src/test/java/org/keycloak/crypto/fips/test/FIPS1402CertificateIdentityExtractorTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2022 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.crypto.fips.test; + +import org.junit.Assume; +import org.junit.Before; +import org.keycloak.authentication.x509.CertificateIdentityExtractorTest; +import org.keycloak.common.util.Environment; + +/** + * @author Marek Posolda + */ +public class FIPS1402CertificateIdentityExtractorTest extends CertificateIdentityExtractorTest { + + @Before + public void before() { + // Run this test just if java is in FIPS mode + Assume.assumeTrue("Java is not in FIPS mode. Skipping the test.", Environment.isJavaInFipsMode()); + } +} diff --git a/docs/fips.md b/docs/fips.md index 7ea3b8948e..f3012532db 100644 --- a/docs/fips.md +++ b/docs/fips.md @@ -126,6 +126,7 @@ mvn clean install -f common -DskipTests=true mvn clean install -f core -DskipTests=true mvn clean install -f server-spi -DskipTests=true mvn clean install -f server-spi-private -DskipTests=true +mvn clean install -f services -DskipTests=true mvn clean install -f crypto/fips1402 ``` diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java index ec4f2b944b..4c986bc4f4 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/X509ClientAuthenticator.java @@ -25,7 +25,6 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -41,15 +40,18 @@ public class X509ClientAuthenticator extends AbstractClientAuthenticator { // These are not recognized by default in RFC1779 or RFC2253 and hence not read in the java by default private static final Map CUSTOM_OIDS = new HashMap<>(); private static final Map CUSTOM_OIDS_REVERSED = new HashMap<>(); + static { CUSTOM_OIDS.put("2.5.4.5", "serialNumber".toUpperCase()); CUSTOM_OIDS.put("2.5.4.15", "businessCategory".toUpperCase()); CUSTOM_OIDS.put("1.3.6.1.4.1.311.60.2.1.3", "jurisdictionCountryName".toUpperCase()); + CUSTOM_OIDS.put("1.2.840.113549.1.9.1", "emailAddress".toUpperCase()); // Reverse map for (Map.Entry entry : CUSTOM_OIDS.entrySet()) { CUSTOM_OIDS_REVERSED.put(entry.getValue(), entry.getKey()); } + CUSTOM_OIDS_REVERSED.put("E", "1.2.840.113549.1.9.1"); // Another synonym for "EMAILADDRESS" } protected static ServicesLogger logger = ServicesLogger.LOGGER; diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java index 50383bc796..a0d0030430 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/AbstractX509ClientCertificateAuthenticator.java @@ -149,7 +149,7 @@ public abstract class AbstractX509ClientCertificateAuthenticator implements Auth private static Function getIssuerDNFunc(X509AuthenticatorConfigModel config) { return config.isCanonicalDnEnabled() ? certs -> certs[0].getIssuerX500Principal().getName(X500Principal.CANONICAL) : - certs -> certs[0].getIssuerDN().getName(); + certs -> certs[0].getIssuerDN().toString(); } static UserIdentityExtractor fromConfig(X509AuthenticatorConfigModel config) { @@ -168,7 +168,7 @@ public abstract class AbstractX509ClientCertificateAuthenticator implements Auth case SUBJECTDN: func = config.isCanonicalDnEnabled() ? certs -> certs[0].getSubjectX500Principal().getName(X500Principal.CANONICAL) : - certs -> certs[0].getSubjectDN().getName(); + certs -> certs[0].getSubjectDN().toString(); extractor = userIdExtractor.getPatternIdentityExtractor(pattern, func); break; case ISSUERDN: diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/CertificateValidator.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/CertificateValidator.java index 9db6fbe839..ddcfcb15e8 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/CertificateValidator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/CertificateValidator.java @@ -67,12 +67,11 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.keycloak.common.crypto.CryptoIntegration; +import org.keycloak.common.util.PemUtils; import org.keycloak.common.util.Time; import org.keycloak.connections.httpclient.HttpClientProvider; import org.keycloak.models.Constants; import org.keycloak.models.KeycloakSession; -import org.keycloak.saml.common.exceptions.ProcessingException; -import org.keycloak.saml.processing.core.util.XMLSignatureUtil; import org.keycloak.services.ServicesLogger; import org.keycloak.truststore.TruststoreProvider; import org.keycloak.utils.CRLUtils; @@ -982,13 +981,11 @@ public class CertificateValidator { public GotOCSPFailOpen oCSPResponseCertificate(String responderCert) { if (responderCert != null && !responderCert.isEmpty()) { try { - _responderCert = XMLSignatureUtil.getX509CertificateFromKeyInfoString(responderCert); + _responderCert = PemUtils.decodeCertificate(responderCert); _responderCert.checkValidity(); } catch(CertificateException e) { logger.warnf("Ignoring invalid certificate: %s", _responderCert); _responderCert = null; - } catch (ProcessingException e) { - throw new RuntimeException(e); } } return new GotOCSPFailOpen(); diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509ClientCertificateAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509ClientCertificateAuthenticator.java index 688ee07259..a4194d2350 100644 --- a/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509ClientCertificateAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/x509/X509ClientCertificateAuthenticator.java @@ -165,13 +165,11 @@ public class X509ClientCertificateAuthenticator extends AbstractX509ClientCertif // Check whether to display the identity confirmation if (!config.getConfirmationPageDisallowed()) { - // FIXME calling forceChallenge was the only way to display + // Calling forceChallenge was the only way to display // a form to let users either choose the user identity from certificate // or to ignore it and proceed to a normal login screen. Attempting // to call the method "challenge" results in a wrong/unexpected behavior. - // The question is whether calling "forceChallenge" here is ok from - // the design viewpoint? - context.forceChallenge(createSuccessResponse(context, certs[0].getSubjectDN().getName())); + context.forceChallenge(createSuccessResponse(context, certs[0].getSubjectDN().toString())); // Do not set the flow status yet, we want to display a form to let users // choose whether to accept the identity from certificate or to specify username/password explicitly } diff --git a/services/src/test/java/org/keycloak/authentication/authenticators/x509/CertificatePemIdentityExtractorTest.java b/services/src/test/java/org/keycloak/authentication/authenticators/x509/CertificatePemIdentityExtractorTest.java deleted file mode 100644 index d4b12b20cd..0000000000 --- a/services/src/test/java/org/keycloak/authentication/authenticators/x509/CertificatePemIdentityExtractorTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.keycloak.authentication.authenticators.x509; - -import static org.junit.Assert.assertEquals; - -import java.io.InputStream; -import java.nio.charset.Charset; -import java.security.cert.X509Certificate; - -import org.junit.ClassRule; -import org.keycloak.rule.CryptoInitRule; -import org.junit.Test; -import org.keycloak.common.crypto.CryptoIntegration; -import org.keycloak.common.crypto.UserIdentityExtractor; -import org.keycloak.common.util.PemUtils; -import org.keycloak.common.util.StreamUtil; - -public class CertificatePemIdentityExtractorTest { - - @ClassRule - public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); - - @Test - public void testExtractsCertInPemFormat() throws Exception { - InputStream is = getClass().getResourceAsStream("/certs/UPN-cert.pem"); - X509Certificate x509Certificate = PemUtils.decodeCertificate(StreamUtil.readString(is, Charset.defaultCharset())); - String certificatePem = PemUtils.encodeCertificate(x509Certificate); - - //X509AuthenticatorConfigModel config = new X509AuthenticatorConfigModel(); - UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getCertificatePemIdentityExtractor(); - - String userIdentity = (String) extractor.extractUserIdentity(new X509Certificate[]{x509Certificate}); - - assertEquals(certificatePem, userIdentity); - } - -} diff --git a/services/src/test/java/org/keycloak/authentication/authenticators/x509/SubjectAltNameIdentityExtractorTest.java b/services/src/test/java/org/keycloak/authentication/authenticators/x509/SubjectAltNameIdentityExtractorTest.java deleted file mode 100644 index e31e4e47c8..0000000000 --- a/services/src/test/java/org/keycloak/authentication/authenticators/x509/SubjectAltNameIdentityExtractorTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2017 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.authentication.authenticators.x509; - -import java.io.InputStream; -import java.nio.charset.Charset; -import java.security.cert.X509Certificate; -import org.junit.Assert; -import org.junit.ClassRule; -import org.junit.Test; -import org.keycloak.common.crypto.CryptoIntegration; -import org.keycloak.common.crypto.UserIdentityExtractor; -import org.keycloak.common.util.PemUtils; -import org.keycloak.common.util.StreamUtil; -import org.keycloak.rule.CryptoInitRule; - -/** - * @author Marek Posolda - */ -public class SubjectAltNameIdentityExtractorTest { - - @ClassRule - public static CryptoInitRule cryptoInitRule = new CryptoInitRule(); - - @Test - public void testX509SubjectAltName_otherName() throws Exception { - UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getSubjectAltNameExtractor(0); - - X509Certificate cert = getCertificate(); - - Object upn = extractor.extractUserIdentity(new X509Certificate[] { cert}); - Assert.assertEquals("test-user@some-company-domain", upn); - } - - - @Test - public void testX509SubjectAltName_email() throws Exception { - UserIdentityExtractor extractor = CryptoIntegration.getProvider().getIdentityExtractorProvider().getSubjectAltNameExtractor(1); - - X509Certificate cert = getCertificate(); - - Object upn = extractor.extractUserIdentity(new X509Certificate[] { cert}); - Assert.assertEquals("test@somecompany.com", upn); - } - - - private X509Certificate getCertificate() throws Exception { - InputStream is = getClass().getResourceAsStream("/certs/UPN-cert.pem"); - - String s = StreamUtil.readString(is, Charset.defaultCharset()); - - return PemUtils.decodeCertificate(s); - } - - -} diff --git a/testsuite/integration-arquillian/HOW-TO-RUN.md b/testsuite/integration-arquillian/HOW-TO-RUN.md index d7a2b0232f..6d80f60160 100644 --- a/testsuite/integration-arquillian/HOW-TO-RUN.md +++ b/testsuite/integration-arquillian/HOW-TO-RUN.md @@ -81,16 +81,9 @@ Or slightly longer version (that allows you to specify debugging port as well as and you will be able to attach remote debugger to the test. Unfortunately server and adapter are running in different JVMs, so this won't help to debug those. -### JBoss auth server debugging +### Auth server debugging -When tests are run on JBoss based container (WildFly/EAP) there is possibility to attach a debugger, by default on localhost:5005. - -The server won't wait to attach the debugger. There are some properties what can change the default behaviour. - - -Dauth.server.debug.port=$PORT - -Dauth.server.debug.suspend=y - -More info: http://javahowto.blogspot.cz/2010/09/java-agentlibjdwp-for-attaching.html +See below in the "Quarkus" section. ### JBoss app server debugging @@ -788,7 +781,14 @@ Run tests using the `auth-server-quarkus` profile: Right now, the server runs in a separate process. To debug the server set `auth.server.debug` system property to `true`. -To configure the debugger port, set the `auth.server.debug.port` system property with any valid port number. Default is `5005`. +To configure the debugger port, set the `auth.server.debug.port` system property with any valid port number. Default is `5005`. +Note you can also set port for example to `*:5005` or `my-host:5005` to set the bind host. + +By default, quarkus server is started in the testsuite and you need to attach remote debugger to it during running. You can +use `auth.server.debug.suspend=y` to "suspend" server startup when running testsuite, which means that server startup is blocked +until debugger is attached. + +More info: http://javahowto.blogspot.cz/2010/09/java-agentlibjdwp-for-attaching.html ## Cookies testing In order to reproduce some specific cookies behaviour in browsers (like SameSite policies or 3rd party cookie blocking), diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakQuarkusServerDeployableContainer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakQuarkusServerDeployableContainer.java index ef3527c52e..50333c1430 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakQuarkusServerDeployableContainer.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/KeycloakQuarkusServerDeployableContainer.java @@ -27,7 +27,9 @@ import java.security.NoSuchAlgorithmException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -174,7 +176,7 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta } private Process startContainer() throws IOException { - ProcessBuilder pb = new ProcessBuilder(getProcessCommands()); + ProcessBuilder pb = getProcessBuilder(); File wrkDir = configuration.getProvidersPath().resolve("bin").toFile(); ProcessBuilder builder = pb.directory(wrkDir).redirectErrorStream(true); @@ -199,8 +201,10 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta return builder.start(); } - private String[] getProcessCommands() { + private ProcessBuilder getProcessBuilder() { List commands = new ArrayList<>(); + Map env = new HashMap<>(); + commands.add(getCommand()); commands.add("-v"); commands.add("start"); @@ -209,10 +213,13 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta if (Boolean.parseBoolean(System.getProperty("auth.server.debug", "false"))) { commands.add("--debug"); - if (configuration.getDebugPort() > 0) { - commands.add(Integer.toString(configuration.getDebugPort())); - } else { - commands.add(System.getProperty("auth.server.debug.port", "5005")); + + String debugPort = configuration.getDebugPort() > 0 ? Integer.toString(configuration.getDebugPort()) : System.getProperty("auth.server.debug.port", "5005"); + env.put("DEBUG_PORT", debugPort); + + String debugSuspend = System.getProperty("auth.server.debug.suspend"); + if (debugSuspend != null) { + env.put("DEBUG_SUSPEND", debugSuspend); } } @@ -256,7 +263,12 @@ public class KeycloakQuarkusServerDeployableContainer implements DeployableConta log.debugf("Quarkus parameters: %s", commands); - return commands.toArray(new String[0]); + String[] processCommands = commands.toArray(new String[0]); + + ProcessBuilder pb = new ProcessBuilder(processCommands); + pb.environment().putAll(env); + + return pb; } private void addStorageOptions(StoreProvider storeProvider, List commands) { diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/FAPI1Test.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/FAPI1Test.java index 1c0d159b5a..f196ffe70e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/FAPI1Test.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/FAPI1Test.java @@ -616,6 +616,7 @@ public class FAPI1Test extends AbstractClientPoliciesTest { OIDCAdvancedConfigWrapper clientConfig = OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep); clientConfig.setRequestUris(Collections.singletonList(TestApplicationResourceUrls.clientRequestUri())); clientConfig.setTlsClientAuthSubjectDn("EMAILADDRESS=contact@keycloak.org, CN=Keycloak Intermediate CA, OU=Keycloak, O=Red Hat, ST=MA, C=US"); + clientConfig.setAllowRegexPatternComparison(false); }); ClientResource clientResource = adminClient.realm(REALM_NAME).clients().get(clientUUID); ClientRepresentation client = clientResource.toRepresentation(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/FAPICIBATest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/FAPICIBATest.java index 463aa0d603..b695e76f75 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/FAPICIBATest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/FAPICIBATest.java @@ -370,6 +370,7 @@ public class FAPICIBATest extends AbstractClientPoliciesTest { OIDCAdvancedConfigWrapper clientConfig = OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep); clientConfig.setRequestUris(Collections.singletonList(TestApplicationResourceUrls.clientRequestUri())); clientConfig.setTlsClientAuthSubjectDn("EMAILADDRESS=contact@keycloak.org, CN=Keycloak Intermediate CA, OU=Keycloak, O=Red Hat, ST=MA, C=US"); + clientConfig.setAllowRegexPatternComparison(false); setClientAuthMethodNeutralSettings(clientRep); }); ClientResource clientResource = adminClient.realm(REALM_NAME).clients().get(clientUUID); @@ -413,6 +414,7 @@ public class FAPICIBATest extends AbstractClientPoliciesTest { OIDCAdvancedConfigWrapper clientConfig = OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep); clientConfig.setRequestUris(Collections.singletonList(TestApplicationResourceUrls.clientRequestUri())); clientConfig.setTlsClientAuthSubjectDn("EMAILADDRESS=contact@keycloak.org, CN=Keycloak Intermediate CA, OU=Keycloak, O=Red Hat, ST=MA, C=US"); + clientConfig.setAllowRegexPatternComparison(false); setClientAuthMethodNeutralSettings(clientRep); }); ClientResource clientResource = adminClient.realm(REALM_NAME).clients().get(clientUUID); @@ -442,6 +444,7 @@ public class FAPICIBATest extends AbstractClientPoliciesTest { OIDCAdvancedConfigWrapper clientConfig = OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep); clientConfig.setRequestUris(Collections.singletonList(TestApplicationResourceUrls.clientRequestUri())); clientConfig.setTlsClientAuthSubjectDn("EMAILADDRESS=contact@keycloak.org, CN=Keycloak Intermediate CA, OU=Keycloak, O=Red Hat, ST=MA, C=US"); + clientConfig.setAllowRegexPatternComparison(false); setClientAuthMethodNeutralSettings(clientRep); }); ClientResource clientResource = adminClient.realm(REALM_NAME).clients().get(clientUUID); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/MutualTLSClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/MutualTLSClientTest.java index 0263e5c252..180ad75a7e 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/MutualTLSClientTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/MutualTLSClientTest.java @@ -67,12 +67,16 @@ public class MutualTLSClientTest extends AbstractTestRealmKeycloakTest { exactSubjectDNConfiguration.setServiceAccountsEnabled(Boolean.TRUE); exactSubjectDNConfiguration.setRedirectUris(Arrays.asList("https://localhost:8543/auth/realms/master/app/auth")); exactSubjectDNConfiguration.setClientAuthenticatorType(X509ClientAuthenticator.PROVIDER_ID); - exactSubjectDNConfiguration.setAttributes(Collections.singletonMap(X509ClientAuthenticator.ATTR_SUBJECT_DN, EXACT_CERTIFICATE_SUBJECT_DN)); + Map attrs = new HashMap<>(); + attrs.put(X509ClientAuthenticator.ATTR_SUBJECT_DN, EXACT_CERTIFICATE_SUBJECT_DN); + attrs.put(X509ClientAuthenticator.ATTR_ALLOW_REGEX_PATTERN_COMPARISON, "false"); + exactSubjectDNConfiguration.setAttributes(attrs); ClientRepresentation obbSubjectDNConfiguration = KeycloakModelUtils.createClient(testRealm, OBB_SUBJECT_DN_CLIENT_ID); obbSubjectDNConfiguration.setServiceAccountsEnabled(Boolean.TRUE); obbSubjectDNConfiguration.setRedirectUris(Arrays.asList("https://localhost:8543/auth/realms/master/app/auth")); obbSubjectDNConfiguration.setClientAuthenticatorType(X509ClientAuthenticator.PROVIDER_ID); + obbSubjectDNConfiguration.setAttributes(Collections.singletonMap(X509ClientAuthenticator.ATTR_ALLOW_REGEX_PATTERN_COMPARISON, "false")); // ATTR_SUBJECT_DN will be set in the individual tests based on the requested Subject DN Format } @@ -193,7 +197,6 @@ public class MutualTLSClientTest extends AbstractTestRealmKeycloakTest { ClientResource client = ApiUtil.findClientByClientId(testRealm(), OBB_SUBJECT_DN_CLIENT_ID); ClientRepresentation clientRep = client.toRepresentation(); OIDCAdvancedConfigWrapper config = OIDCAdvancedConfigWrapper.fromClientRepresentation(clientRep); - config.setAllowRegexPatternComparison(false); config.setTlsClientAuthSubjectDn(expectedSubjectDN); client.update(clientRep);