From b5a5d68dbc06390d26c1c0b628e4544f718ebd3a Mon Sep 17 00:00:00 2001 From: Pedro Igor Date: Thu, 5 May 2022 16:18:17 -0300 Subject: [PATCH] Imposing certain constraints to files when importing at start-up Closes #11861 --- docs/guides/src/main/server/importExport.adoc | 2 +- .../QuarkusJpaConnectionProviderFactory.java | 18 ++++++-- .../it/cli/dist/ImportAtStartupDistTest.java | 44 ++++++++++++++++--- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/docs/guides/src/main/server/importExport.adoc b/docs/guides/src/main/server/importExport.adoc index 2963dd0da2..e77b45df5f 100644 --- a/docs/guides/src/main/server/importExport.adoc +++ b/docs/guides/src/main/server/importExport.adoc @@ -80,7 +80,7 @@ You are also able to import realms when the server is starting by using the `--i <@kc.start parameters="--import-realm"/> When you set the `--import-realm` option, the server is going to try to import any realm configuration file from the `data/import` directory. Each file in this directory should -contain a single realm configuration. +contain a single realm configuration. Only regular files using the `.json` extension are read from this directory, sub-directories are ignored. If a realm already exists in the server, the import operation is skipped. diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java index 303400b368..aa18698fc4 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java @@ -25,6 +25,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Connection; import java.sql.DatabaseMetaData; @@ -58,10 +59,14 @@ import org.keycloak.connections.jpa.JpaConnectionProvider; import org.keycloak.connections.jpa.updater.JpaUpdaterProvider; import org.keycloak.connections.jpa.util.JpaUtils; import org.keycloak.exportimport.ExportImportManager; -import org.keycloak.migration.MigrationModel; import org.keycloak.migration.MigrationModelManager; import org.keycloak.migration.ModelVersion; -import org.keycloak.models.*; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.ModelDuplicateException; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.UserProvider; import org.keycloak.models.dblock.DBLockManager; import org.keycloak.models.dblock.DBLockProvider; import org.keycloak.models.utils.RepresentationToModel; @@ -322,8 +327,15 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr String file = tokenizer.nextToken().trim(); RealmRepresentation rep; try { + Path filePath = Paths.get(file); + + if (!(Files.exists(filePath) && Files.isRegularFile(filePath) && filePath.toString().endsWith(".json"))) { + logger.debugf("Ignoring import file because it is not a valid file: %s", file); + continue; + } + rep = JsonSerialization.readValue(StringPropertyReplacer.replaceProperties( - Files.readString(Paths.get(file)), new StringPropertyReplacer.PropertyResolver() { + Files.readString(filePath), new StringPropertyReplacer.PropertyResolver() { @Override public String resolve(String property) { return Optional.ofNullable(System.getenv(property)).orElse(null); diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java index 5a1f2b717a..0907856db3 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportAtStartupDistTest.java @@ -18,22 +18,19 @@ package org.keycloak.it.cli.dist; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.function.Consumer; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; import org.keycloak.it.junit5.extension.BeforeStartDistribution; import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.utils.KeycloakDistribution; +import org.keycloak.it.utils.RawKeycloakDistribution; import io.quarkus.test.junit.main.Launch; import io.quarkus.test.junit.main.LaunchResult; -@DistributionTest +@DistributionTest(reInstall = DistributionTest.ReInstall.BEFORE_TEST) @RawDistOnly(reason = "Containers are immutable") public class ImportAtStartupDistTest { @@ -45,6 +42,23 @@ public class ImportAtStartupDistTest { cliResult.assertMessage("Imported realm quickstart-realm from file"); } + @Test + @BeforeStartDistribution(CreateRealmConfigurationFileAndDir.class) + @Launch({"start-dev", "--import-realm", "--log-level=org.keycloak.quarkus.runtime.storage.database.jpa:debug"}) + void testImportAndIgnoreDirectory(LaunchResult result) { + CLIResult cliResult = (CLIResult) result; + cliResult.assertMessage("Imported realm quickstart-realm from file"); + cliResult.assertMessage("Ignoring import file because it is not a valid file"); + } + + @Test + @BeforeStartDistribution(CreateRealmConfigurationFileWithUnsupportedExtension.class) + @Launch({"start-dev", "--import-realm", "--log-level=org.keycloak.quarkus.runtime.storage.database.jpa:debug"}) + void testIgnoreFileWithUnsupportedExtension(LaunchResult result) { + CLIResult cliResult = (CLIResult) result; + cliResult.assertMessage("Ignoring import file because it is not a valid file"); + } + @Test @BeforeStartDistribution(CreateRealmConfigurationFile.class) @Launch({"start-dev", "--import-realm", "some-file"}) @@ -60,4 +74,24 @@ public class ImportAtStartupDistTest { distribution.copyOrReplaceFileFromClasspath("/quickstart-realm.json", Path.of("data", "import", "realm.json")); } } + + public static class CreateRealmConfigurationFileAndDir implements Consumer { + + @Override + public void accept(KeycloakDistribution distribution) { + distribution.copyOrReplaceFileFromClasspath("/quickstart-realm.json", Path.of("data", "import", "realm.json")); + + RawKeycloakDistribution rawDist = (RawKeycloakDistribution) distribution; + + rawDist.getDistPath().resolve("data").resolve("import").resolve("sub-dir").toFile().mkdirs(); + } + } + + public static class CreateRealmConfigurationFileWithUnsupportedExtension implements Consumer { + + @Override + public void accept(KeycloakDistribution distribution) { + distribution.copyOrReplaceFileFromClasspath("/quickstart-realm.json", Path.of("data", "import", "realm")); + } + } }