NoSuchFileException with ${kc.home.dir} on Windows (#24080)

* NoSuchFileException with ${kc.home.dir} on Windows

* added a path validation for https cert and key files in HttpPropertyMappers

Closes #23217

Signed-off-by: Peter Zaoral <pzaoral@redhat.com>

* NoSuchFileException with ${kc.home.dir} on Windows

* added a path validation for https cert and key files in HttpPropertyMappers

Closes #23217

Signed-off-by: Peter Zaoral <pzaoral@redhat.com>

* NoSuchFileException with ${kc.home.dir} on Windows

* added a path validation for https cert and key files in HttpPropertyMappers

Closes #23217

Signed-off-by: Peter Zaoral <pzaoral@redhat.com>

---------

Signed-off-by: Peter Zaoral <pzaoral@redhat.com>
This commit is contained in:
Peter Zaoral 2023-11-30 19:47:41 +01:00 committed by GitHub
parent d79e414199
commit 386e4ea8d4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 55 additions and 11 deletions

View file

@ -59,12 +59,12 @@ public class HttpOptions {
.defaultValue("TLSv1.3,TLSv1.2")
.build();
public static final Option HTTPS_CERTIFICATE_FILE = new OptionBuilder<>("https-certificate-file", File.class)
public static final Option<File> HTTPS_CERTIFICATE_FILE = new OptionBuilder<>("https-certificate-file", File.class)
.category(OptionCategory.HTTP)
.description("The file path to a server certificate or certificate chain in PEM format.")
.build();
public static final Option HTTPS_CERTIFICATE_KEY_FILE = new OptionBuilder<>("https-certificate-key-file", File.class)
public static final Option<File> HTTPS_CERTIFICATE_KEY_FILE = new OptionBuilder<>("https-certificate-key-file", File.class)
.category(OptionCategory.HTTP)
.description("The file path to a private key in PEM format.")
.build();

View file

@ -13,6 +13,7 @@ import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider;
import java.io.File;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.function.BiFunction;
import static java.util.Optional.empty;
import static java.util.Optional.of;
@ -21,6 +22,8 @@ import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers
import static org.keycloak.quarkus.runtime.integration.QuarkusPlatform.addInitializationException;
final class HttpPropertyMappers {
private static final String QUARKUS_HTTPS_CERT_FILES = "quarkus.http.ssl.certificate.files";
private static final String QUARKUS_HTTPS_CERT_KEY_FILES = "quarkus.http.ssl.certificate.key-files";
private HttpPropertyMappers(){}
@ -64,15 +67,17 @@ final class HttpPropertyMappers {
.paramLabel("protocols")
.build(),
fromOption(HttpOptions.HTTPS_CERTIFICATE_FILE)
.to("quarkus.http.ssl.certificate.files")
.to(QUARKUS_HTTPS_CERT_FILES)
.transformer(HttpPropertyMappers.validatePath(QUARKUS_HTTPS_CERT_FILES))
.paramLabel("file")
.build(),
fromOption(HttpOptions.HTTPS_CERTIFICATE_KEY_FILE)
.to("quarkus.http.ssl.certificate.key-files")
.to(QUARKUS_HTTPS_CERT_KEY_FILES)
.transformer(HttpPropertyMappers.validatePath(QUARKUS_HTTPS_CERT_KEY_FILES))
.paramLabel("file")
.build(),
fromOption(HttpOptions.HTTPS_KEY_STORE_FILE
.withRuntimeSpecificDefault(getDefaultKeystorePathValue()))
.withRuntimeSpecificDefault(getDefaultKeystorePathValue()))
.to("quarkus.http.ssl.certificate.key-store-file")
.paramLabel("file")
.build(),
@ -109,6 +114,10 @@ final class HttpPropertyMappers {
};
}
private static BiFunction<Optional<String>, ConfigSourceInterceptorContext, Optional<String>> validatePath(String key) {
return (value, context) -> Environment.isWindows() ? value.filter(v -> v.equals(context.proceed(key).getValue())).map(p -> p.replace("\\", "/")) : value;
}
private static Optional<String> getHttpEnabledTransformer(Optional<String> value, ConfigSourceInterceptorContext context) {
boolean enabled = Boolean.parseBoolean(value.get());
ConfigValue proxy = context.proceed(MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX + "proxy");

View file

@ -28,6 +28,8 @@ import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.OS;
import org.keycloak.it.junit5.extension.BeforeStartDistribution;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest;
@ -105,7 +107,7 @@ public class QuarkusPropertiesDistTest {
@Test
@BeforeStartDistribution(UpdateHibernateMetricsFromQuarkusProps.class)
@Launch({ "build", "--metrics-enabled=true" })
@Order(8)
@Order(7)
void buildFirstWithUnknownQuarkusBuildProperty(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertBuild();
@ -114,7 +116,7 @@ public class QuarkusPropertiesDistTest {
@Test
@KeepServerAlive
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false", OPTIMIZED_BUILD_OPTION_LONG})
@Order(9)
@Order(8)
void testUnknownQuarkusBuildTimePropertyApplied(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertNoBuild();
@ -124,7 +126,7 @@ public class QuarkusPropertiesDistTest {
@Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--config-keystore=../../../../src/test/resources/keystore" })
@Order(10)
@Order(9)
void testMissingSmallRyeKeyStorePasswordProperty(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertError("config-keystore-password must be specified");
@ -133,7 +135,7 @@ public class QuarkusPropertiesDistTest {
@Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--config-keystore-password=secret" })
@Order(11)
@Order(10)
void testMissingSmallRyeKeyStorePathProperty(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertError("config-keystore must be specified");
@ -143,7 +145,7 @@ public class QuarkusPropertiesDistTest {
@Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false", "--config-keystore=/invalid/path",
"--config-keystore-password=secret" })
@Order(12)
@Order(11)
void testInvalidSmallRyeKeyStorePathProperty(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertError("java.lang.IllegalArgumentException: config-keystore path does not exist: /invalid/path");
@ -153,7 +155,7 @@ public class QuarkusPropertiesDistTest {
@Test
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false",
"--config-keystore=../../../../src/test/resources/keystore", "--config-keystore-password=secret" })
@Order(13)
@Order(12)
void testSmallRyeKeyStoreConfigSource(LaunchResult result) {
// keytool -importpass -alias kc.log-level -keystore keystore -storepass secret -storetype PKCS12 -v (with "debug" as the stored password)
CLIResult cliResult = (CLIResult) result;
@ -161,6 +163,30 @@ public class QuarkusPropertiesDistTest {
cliResult.assertBuild();
}
@Test
@BeforeStartDistribution(ForceRebuild.class)
@DisabledOnOs(value = { OS.WINDOWS }, disabledReason = "Windows uses a different path separator.")
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false",
"--https-certificate-file=/tmp/kc/bin/../conf/server.crt.pem",
"--https-certificate-key-file=/tmp/kc/bin/../conf/server.key.pem" })
@Order(13)
void testHttpCertsPathTransformer(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertTrue(cliResult.getOutput().contains("ERROR: /tmp/kc/bin/../conf/server.crt.pem"));
}
@Test
@BeforeStartDistribution(ForceRebuild.class)
@DisabledOnOs(value = { OS.LINUX, OS.MAC }, disabledReason = "Windows uses a different path separator.")
@Launch({ "start", "--http-enabled=true", "--hostname-strict=false",
"--https-certificate-file=C:\\tmp\\kc\\bin\\..\\conf/server.crt.pem",
"--https-certificate-key-file=C:\\tmp\\kc\\bin\\..\\conf/server.key.pem" })
@Order(14)
void testHttpCertsPathTransformerOnWindows(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
assertTrue(cliResult.getOutput().contains("ERROR: C:/tmp/kc/bin/../conf/server.crt.pem"));
}
public static class UpdateConsoleLogLevelToWarnFromQuarkusProps implements Consumer<KeycloakDistribution> {
@Override
public void accept(KeycloakDistribution distribution) {
@ -194,4 +220,13 @@ public class QuarkusPropertiesDistTest {
distribution.setQuarkusProperty(QUARKUS_BUILDTIME_HIBERNATE_METRICS_KEY, "true");
}
}
public static class ForceRebuild implements Consumer<KeycloakDistribution> {
@Override
public void accept(KeycloakDistribution distribution) {
CLIResult buildResult = distribution.run("build");
buildResult.assertBuild();
}
}
}