Imposing certain constraints to files when importing at start-up

Closes #11861
This commit is contained in:
Pedro Igor 2022-05-05 16:18:17 -03:00
parent 3feed3827c
commit b5a5d68dbc
3 changed files with 55 additions and 9 deletions

View file

@ -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"/> <@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 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. If a realm already exists in the server, the import operation is skipped.

View file

@ -25,6 +25,7 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DatabaseMetaData; 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.updater.JpaUpdaterProvider;
import org.keycloak.connections.jpa.util.JpaUtils; import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.exportimport.ExportImportManager; import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.migration.MigrationModel;
import org.keycloak.migration.MigrationModelManager; import org.keycloak.migration.MigrationModelManager;
import org.keycloak.migration.ModelVersion; 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.DBLockManager;
import org.keycloak.models.dblock.DBLockProvider; import org.keycloak.models.dblock.DBLockProvider;
import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.models.utils.RepresentationToModel;
@ -322,8 +327,15 @@ public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionPr
String file = tokenizer.nextToken().trim(); String file = tokenizer.nextToken().trim();
RealmRepresentation rep; RealmRepresentation rep;
try { 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( rep = JsonSerialization.readValue(StringPropertyReplacer.replaceProperties(
Files.readString(Paths.get(file)), new StringPropertyReplacer.PropertyResolver() { Files.readString(filePath), new StringPropertyReplacer.PropertyResolver() {
@Override @Override
public String resolve(String property) { public String resolve(String property) {
return Optional.ofNullable(System.getenv(property)).orElse(null); return Optional.ofNullable(System.getenv(property)).orElse(null);

View file

@ -18,22 +18,19 @@
package org.keycloak.it.cli.dist; package org.keycloak.it.cli.dist;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer; 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.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.keycloak.it.junit5.extension.BeforeStartDistribution; import org.keycloak.it.junit5.extension.BeforeStartDistribution;
import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.DistributionTest; import org.keycloak.it.junit5.extension.DistributionTest;
import org.keycloak.it.junit5.extension.RawDistOnly; import org.keycloak.it.junit5.extension.RawDistOnly;
import org.keycloak.it.utils.KeycloakDistribution; 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.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
@DistributionTest @DistributionTest(reInstall = DistributionTest.ReInstall.BEFORE_TEST)
@RawDistOnly(reason = "Containers are immutable") @RawDistOnly(reason = "Containers are immutable")
public class ImportAtStartupDistTest { public class ImportAtStartupDistTest {
@ -45,6 +42,23 @@ public class ImportAtStartupDistTest {
cliResult.assertMessage("Imported realm quickstart-realm from file"); 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 @Test
@BeforeStartDistribution(CreateRealmConfigurationFile.class) @BeforeStartDistribution(CreateRealmConfigurationFile.class)
@Launch({"start-dev", "--import-realm", "some-file"}) @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")); distribution.copyOrReplaceFileFromClasspath("/quickstart-realm.json", Path.of("data", "import", "realm.json"));
} }
} }
public static class CreateRealmConfigurationFileAndDir implements Consumer<KeycloakDistribution> {
@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<KeycloakDistribution> {
@Override
public void accept(KeycloakDistribution distribution) {
distribution.copyOrReplaceFileFromClasspath("/quickstart-realm.json", Path.of("data", "import", "realm"));
}
}
} }