From ea3937f37c31eed4df605b1fd09a6e8703dfc06b Mon Sep 17 00:00:00 2001 From: Steven Hawkins Date: Mon, 12 Aug 2024 12:20:47 -0400 Subject: [PATCH] fix: always replacing placeholders (#31871) closes: #31625 Signed-off-by: Steve Hawkins --- .../common/util/StringPropertyReplacer.java | 30 +---------------- .../topics/changes/changes-26_0_0.adoc | 6 ++++ docs/guides/server/importExport.adoc | 32 +++++++++---------- ...ycloakRealmImportJobDependentResource.java | 4 +-- .../quarkus/runtime/cli/command/Import.java | 5 ++- .../keycloak/it/cli/dist/ImportDistTest.java | 25 ++++++++++++--- 6 files changed, 49 insertions(+), 53 deletions(-) diff --git a/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java b/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java index e4c8adfc4da..c48d02212d4 100755 --- a/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java +++ b/common/src/main/java/org/keycloak/common/util/StringPropertyReplacer.java @@ -101,12 +101,7 @@ public final class StringPropertyReplacer if (props == null) { return replaceProperties(string, (PropertyResolver) null); } - return replaceProperties(string, new PropertyResolver() { - @Override - public String resolve(String property) { - return props.getProperty(property); - } - }); + return replaceProperties(string, props::getProperty); } public static String replaceProperties(final String string, PropertyResolver resolver) @@ -249,29 +244,6 @@ public final class StringPropertyReplacer return buffer.toString(); } - /** - * Try to resolve a "key" from the provided properties by - * checking if it is actually a "key1,key2", in which case - * try first "key1", then "key2". If all fails, return null. - * - * It also accepts "key1," and ",key2". - * - * @param key the key to resolve - * @param props the properties to use - * @return the resolved key or null - */ - private static String resolveCompositeKey(String key, final Properties props) { - if (props == null) { - return resolveCompositeKey(key, (PropertyResolver) null); - } - return resolveCompositeKey(key, new PropertyResolver() { - @Override - public String resolve(String property) { - return props.getProperty(property); - } - }); - } - private static String resolveCompositeKey(String key, PropertyResolver resolver) { String value = null; diff --git a/docs/documentation/upgrading/topics/changes/changes-26_0_0.adoc b/docs/documentation/upgrading/topics/changes/changes-26_0_0.adoc index 8bc3e439edd..d74fa1823fb 100644 --- a/docs/documentation/upgrading/topics/changes/changes-26_0_0.adoc +++ b/docs/documentation/upgrading/topics/changes/changes-26_0_0.adoc @@ -133,3 +133,9 @@ when exporting a realm. To obtain the query the identity providers in a realm, prefer using the `/realms/{realm}/identity-provider/instances` endpoint. This endpoint supports filters and pagination. + += CLI import placeholder replacement + +The CLI command `kc.[sh|bat] import` now has placeholder replacement enabled. Previously placeholder replacement was only enabled for realm import at startup. + +If you wish to disable placeholder replacement for the `import` command, add the system property `-Dkeycloak.migration.replace-placeholders=false` diff --git a/docs/guides/server/importExport.adoc b/docs/guides/server/importExport.adoc index 9d1de49c152..2100656d4f0 100644 --- a/docs/guides/server/importExport.adoc +++ b/docs/guides/server/importExport.adoc @@ -94,6 +94,22 @@ To import a realm previously exported in a single file, you can use the `--file <@kc.import parameters="--file "/> +== Using Environment Variables within the Realm Configuration Files + +You are able to use placeholders to resolve values from environment variables for any realm configuration. + +.Realm configuration using placeholders +[source, bash] +---- +{ + "realm": "${r"${MY_REALM_NAME}"}", + "enabled": true, + ... +} +---- + +In the example above, the value set to the `MY_REALM_NAME` environment variable is going to be used to set the `realm` property. + == Importing a Realm during Startup You are also able to import realms when the server is starting by using the `--import-realm` option. @@ -111,22 +127,6 @@ To re-create realms you should explicitly run the `import` command prior to star Importing the `master` realm is not supported because as it is a very sensitive operation. -=== Using Environment Variables within the Realm Configuration Files - -When importing a realm at startup, you are able to use placeholders to resolve values from environment variables for any realm configuration. - -.Realm configuration using placeholders -[source, bash] ----- -{ - "realm": "${r"${MY_REALM_NAME}"}", - "enabled": true, - ... -} ----- - -In the example above, the value set to the `MY_REALM_NAME` environment variable is going to be used to set the `realm` property. - == Importing and Exporting by using the Admin Console You can also import and export a realm using the Admin Console. This functionality is diff --git a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportJobDependentResource.java b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportJobDependentResource.java index 476c49a7fc1..14df933a7e5 100644 --- a/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportJobDependentResource.java +++ b/operator/src/main/java/org/keycloak/operator/controllers/KeycloakRealmImportJobDependentResource.java @@ -149,10 +149,8 @@ public class KeycloakRealmImportJobDependentResource extends KubernetesDependent var runBuild = !keycloakContainer.getArgs().contains(KeycloakDeploymentDependentResource.OPTIMIZED_ARG) ? "/opt/keycloak/bin/kc.sh --verbose build && " : ""; - var replaceOption = (replacePlaceholders) ? " -Dkeycloak.migration.replace-placeholders=true": ""; - var commandArgs = List.of("-c", - runBuild + "/opt/keycloak/bin/kc.sh" + replaceOption + " --verbose import --optimized --file='" + importMntPath + keycloakRealmImport.getRealmName() + "-realm.json' " + override); + runBuild + "/opt/keycloak/bin/kc.sh --verbose import --optimized --file='" + importMntPath + keycloakRealmImport.getRealmName() + "-realm.json' " + override); keycloakContainer.setCommand(command); keycloakContainer.setArgs(commandArgs); diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/Import.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/Import.java index 7032f3159d8..cc606a06b9e 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/Import.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/Import.java @@ -35,7 +35,10 @@ public final class Import extends AbstractNonServerCommand implements Runnable { @Override protected void doBeforeRun() { - System.setProperty(ExportImportConfig.ACTION, ACTION_IMPORT); + if (System.getProperty(ExportImportConfig.REPLACE_PLACEHOLDERS) == null) { + ExportImportConfig.setReplacePlaceholders(true); + } + ExportImportConfig.setAction(ACTION_IMPORT); } @Override diff --git a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportDistTest.java b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportDistTest.java index e74594c8ac2..0a32a8d5a44 100644 --- a/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportDistTest.java +++ b/quarkus/tests/integration/src/test/java/org/keycloak/it/cli/dist/ImportDistTest.java @@ -17,28 +17,45 @@ package org.keycloak.it.cli.dist; +import java.io.File; +import java.io.IOException; + import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; 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.junit5.extension.WithEnvVars; import org.keycloak.it.utils.KeycloakDistribution; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; + @DistributionTest @RawDistOnly(reason = "Containers are immutable") @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class ImportDistTest { @Test - void testImport(KeycloakDistribution dist) { + void testImport(KeycloakDistribution dist) throws IOException { CLIResult cliResult = dist.run("build"); + + File dir = new File("target"); - cliResult = dist.run("export", "--realm=master", "--dir=."); + cliResult = dist.run("export", "--realm=master", "--dir=" + dir.getAbsolutePath()); cliResult.assertMessage("Export of realm 'master' requested."); cliResult.assertMessage("Export finished successfully"); - - cliResult = dist.run("import", "--dir=."); + + // add a placeholder into the realm + ObjectMapper mapper = new ObjectMapper(); + File file = new File(dir, "master-realm.json"); + ObjectNode node = (ObjectNode)mapper.readTree(file); + node.put("enabled", "${REALM_ENABLED}"); + mapper.writer().writeValue(file, node); + + dist.setEnvVar("REALM_ENABLED", "true"); + cliResult = dist.run("import", "--dir=" + dir.getAbsolutePath()); cliResult.assertMessage("Realm 'master' imported"); cliResult.assertMessage("Import finished successfully"); cliResult.assertNoMessage("Changes detected in configuration");