correcting handling of \ in configuration values on windows (#34462)

closes: #34041

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
Co-authored-by: Peter Zaoral <pzaoral@redhat.com>
This commit is contained in:
Steven Hawkins 2024-12-04 11:15:05 -05:00 committed by GitHub
parent 30196dfe12
commit 37dc2b6ca3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 29 additions and 10 deletions

View File

@ -155,6 +155,8 @@ Note that some Quarkus properties are already mapped in the {project_name} confi
To disable expression evaluation, the `\` character functions as an escape character. In particular, it must be used to escape the usage of `$` characters when they appear to define an expression or are repeated. For example, if you want the configuration value `my$$password`, use `my\$\$password` instead. Note that the `\` character requires additional escaping or quoting when using most unix shells, or when it appears in properties files.
For example, bash single quotes preserve the single backslash `--db-password='my\$\$password'`. Also, with bash double quotes, you need an additional backslash `--db-password="my\\$\\$password"`. Similarly in a properties file, backslash characters must also be escaped: `kc.db-password=my\\$\\$password`
When specifying Windows file paths in configuration values, backslashes must also be escaped. For example, if you want to specify the path `C:\path\to\file`, you should write it as `C:\\path\\to\\file`. Alternatively, you can use forward slashes which don't need escaping: `C:/path/to/file`.
== Starting {project_name}
You can start {project_name} in `development mode` or `production mode`. Each mode offers different defaults for the intended environment.

View File

@ -177,8 +177,12 @@ if "x%JAVA%" == "x" (
set CLASSPATH_OPTS="%DIRNAME%..\lib\quarkus-run.jar"
rem set the homedir with \ replaced by /
set KC_HOME_DIR=%DIRNAME%..
set KC_HOME_DIR=%KC_HOME_DIR:\=/%
rem The property 'java.util.concurrent.ForkJoinPool.common.threadFactory' is set here, as a Java Agent or enabling JMX might initialize the factory before Quarkus can set the property in JDK21+.
set JAVA_RUN_OPTS=-Djava.util.concurrent.ForkJoinPool.common.threadFactory=io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory %JAVA_OPTS% -Dkc.home.dir="%DIRNAME%.." -Djboss.server.config.dir="%DIRNAME%..\conf" -Dkeycloak.theme.dir="%DIRNAME%..\themes" %SERVER_OPTS% -cp %CLASSPATH_OPTS% io.quarkus.bootstrap.runner.QuarkusEntryPoint %CONFIG_ARGS%
set JAVA_RUN_OPTS=-Djava.util.concurrent.ForkJoinPool.common.threadFactory=io.quarkus.bootstrap.forkjoin.QuarkusForkJoinWorkerThreadFactory %JAVA_OPTS% -Dkc.home.dir="%KC_HOME_DIR%" -Djboss.server.config.dir="%DIRNAME%..\conf" -Dkeycloak.theme.dir="%DIRNAME%..\themes" %SERVER_OPTS% -cp %CLASSPATH_OPTS% io.quarkus.bootstrap.runner.QuarkusEntryPoint %CONFIG_ARGS%
set OPTIMIZED_OPTION=--optimized
set HELP_LONG_OPTION=--help

View File

@ -139,31 +139,31 @@ public final class HttpPropertyMappers {
public static void validateConfig() {
boolean enabled = isHttpEnabled(Configuration.getOptionalKcValue(HttpOptions.HTTP_ENABLED.getKey()).orElse(null));
Optional<String> certFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_CERTIFICATE_FILE.getKey());
Optional<String> keystoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_FILE.getKey());
Optional<String> certFile = Configuration.getOptionalValue(QUARKUS_HTTPS_CERT_FILES);
Optional<String> keystoreFile = Configuration.getOptionalValue(QUARKUS_HTTPS_KEY_STORE_FILE);
if (!enabled && certFile.isEmpty() && keystoreFile.isEmpty()) {
throw new PropertyException(Messages.httpsConfigurationNotSet());
}
CertificateConfig config = new CertificateConfig();
config.trustStoreFile = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_FILE.getKey()).map(Paths::get);
config.trustStoreFile = Configuration.getOptionalValue(QUARKUS_HTTPS_TRUST_STORE_FILE).map(Paths::get);
config.trustStorePassword = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_PASSWORD.getKey());
config.trustStoreFileType = Configuration.getOptionalKcValue(HttpOptions.HTTPS_TRUST_STORE_TYPE.getKey());
config.trustStoreFileType = Configuration.getOptionalValue(QUARKUS_HTTPS_TRUST_STORE_FILE_TYPE);
config.trustStoreProvider = Configuration.getOptionalValue("quarkus.http.ssl.certificate.trust-store-provider");
config.trustStoreCertAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.trust-store-cert-alias");
config.trustStoreFiles = Optional.empty();
config.keyStoreFile = keystoreFile.map(Paths::get);
config.keyStorePassword = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_PASSWORD.getKey());
config.keyStoreFileType = Configuration.getOptionalKcValue(HttpOptions.HTTPS_KEY_STORE_TYPE.getKey());
config.keyStoreFileType = Configuration.getOptionalValue(QUARKUS_HTTPS_KEY_STORE_FILE_TYPE);
config.keyStoreProvider = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-provider");
config.keyStoreAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias");
config.keyStoreAliasPassword = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias-password");
config.keyStoreAliasPasswordKey = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-alias-password-key");
config.keyStoreKeyAlias = Configuration.getOptionalValue("quarkus.http.ssl.certificate.key-store-key-alias");
config.keyFiles = Configuration.getOptionalKcValue(HttpOptions.HTTPS_CERTIFICATE_KEY_FILE.getKey()).map(Paths::get).map(List::of);
config.keyFiles = Configuration.getOptionalValue(QUARKUS_HTTPS_CERT_KEY_FILES).map(Paths::get).map(List::of);
config.files = certFile.map(Paths::get).map(List::of);
try {

View File

@ -22,8 +22,10 @@ import io.quarkus.test.junit.main.LaunchResult;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.DryRun;
import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.junit5.extension.WithEnvVars;
@ -32,6 +34,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.matchesPattern;
@DryRun
@DistributionTest
@RawDistOnly(reason = "No need to test script again on container")
@WithEnvVars({"PRINT_ENV", "true"})
@ -120,4 +123,13 @@ public class JavaOptsScriptTest {
assertThat(output, containsString("DefaultFactory: groovy Closures in annotations are disabled and will not be loaded"));
}
@EnabledOnOs(value = { OS.WINDOWS }, disabledReason = "different path behaviour on Windows.")
@Test
@Launch({"start-dev", "--optimized"})
void testKcHomeDirPathFormat(LaunchResult result) {
String output = result.getOutput();
assertThat(output, containsString("kc.home.dir="));
assertThat(output, matchesPattern("(?s).*kc\\.home\\.dir=\"[A-Z]:/.*?/keycloak/quarkus/tests/integration/target/kc-tests/keycloak-\\d+\\.\\d+\\.\\d+.*?/bin/\\.\\.\".*"));
}
}

View File

@ -179,7 +179,7 @@ public class QuarkusPropertiesDistTest {
"--https-certificate-key-file=C:\\tmp\\kc\\bin\\..\\conf/server.key.pem" })
@Order(14)
void testHttpCertsPathTransformerOnWindows(CLIResult cliResult) {
cliResult.assertError("Failed to load 'https-key-' material: NoSuchFileException C:/tmp/kc/bin/../conf/server.crt.pem");
cliResult.assertError("Failed to load 'https-key-' material: NoSuchFileException C:\\tmp\\kc\\bin\\..\\conf\\server.crt.pem");
}
public static class AddConsoleHandlerFromQuarkusProps implements Consumer<KeycloakDistribution> {

View File

@ -102,7 +102,8 @@ public class CLITestExtension extends QuarkusMainTestExtension {
configureEnvVars(context.getRequiredTestClass().getAnnotation(WithEnvVars.class));
configureEnvVars(context.getRequiredTestMethod().getAnnotation(WithEnvVars.class));
boolean dryRun = context.getRequiredTestMethod().getAnnotation(DryRun.class) != null;
boolean dryRun = context.getRequiredTestClass().getAnnotation(DryRun.class) != null
|| context.getRequiredTestMethod().getAnnotation(DryRun.class) != null;
if (dryRun && isRaw()) {
dist.setEnvVar(DryRunMixin.KC_DRY_RUN_ENV, "true");
dist.setEnvVar(DryRunMixin.KC_DRY_RUN_BUILD_ENV, "true");

View File

@ -25,7 +25,7 @@ import java.lang.annotation.Target;
/**
* {@link DryRun} is used to configure a non-running, non-augmenting distribution
*/
@Target({ ElementType.METHOD })
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface DryRun {