fix: always replacing placeholders (#31871)

closes: #31625

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2024-08-12 12:20:47 -04:00 committed by GitHub
parent 4f03c3bf47
commit ea3937f37c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 49 additions and 53 deletions

View file

@ -101,12 +101,7 @@ public final class StringPropertyReplacer
if (props == null) { if (props == null) {
return replaceProperties(string, (PropertyResolver) null); return replaceProperties(string, (PropertyResolver) null);
} }
return replaceProperties(string, new PropertyResolver() { return replaceProperties(string, props::getProperty);
@Override
public String resolve(String property) {
return props.getProperty(property);
}
});
} }
public static String replaceProperties(final String string, PropertyResolver resolver) public static String replaceProperties(final String string, PropertyResolver resolver)
@ -249,29 +244,6 @@ public final class StringPropertyReplacer
return buffer.toString(); 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) private static String resolveCompositeKey(String key, PropertyResolver resolver)
{ {
String value = null; String value = null;

View file

@ -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. 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. 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`

View file

@ -94,6 +94,22 @@ To import a realm previously exported in a single file, you can use the `--file
<@kc.import parameters="--file <file>"/> <@kc.import parameters="--file <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 == Importing a Realm during Startup
You are also able to import realms when the server is starting by using the `--import-realm` option. 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. 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 == Importing and Exporting by using the Admin Console
You can also import and export a realm using the Admin Console. This functionality is You can also import and export a realm using the Admin Console. This functionality is

View file

@ -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 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", 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.setCommand(command);
keycloakContainer.setArgs(commandArgs); keycloakContainer.setArgs(commandArgs);

View file

@ -35,7 +35,10 @@ public final class Import extends AbstractNonServerCommand implements Runnable {
@Override @Override
protected void doBeforeRun() { protected void doBeforeRun() {
System.setProperty(ExportImportConfig.ACTION, ACTION_IMPORT); if (System.getProperty(ExportImportConfig.REPLACE_PLACEHOLDERS) == null) {
ExportImportConfig.setReplacePlaceholders(true);
}
ExportImportConfig.setAction(ACTION_IMPORT);
} }
@Override @Override

View file

@ -17,28 +17,45 @@
package org.keycloak.it.cli.dist; 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.MethodOrderer;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestMethodOrder;
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.junit5.extension.WithEnvVars;
import org.keycloak.it.utils.KeycloakDistribution; import org.keycloak.it.utils.KeycloakDistribution;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
@DistributionTest @DistributionTest
@RawDistOnly(reason = "Containers are immutable") @RawDistOnly(reason = "Containers are immutable")
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) @TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ImportDistTest { public class ImportDistTest {
@Test @Test
void testImport(KeycloakDistribution dist) { void testImport(KeycloakDistribution dist) throws IOException {
CLIResult cliResult = dist.run("build"); 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 of realm 'master' requested.");
cliResult.assertMessage("Export finished successfully"); 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("Realm 'master' imported");
cliResult.assertMessage("Import finished successfully"); cliResult.assertMessage("Import finished successfully");
cliResult.assertNoMessage("Changes detected in configuration"); cliResult.assertNoMessage("Changes detected in configuration");