Export users throws Disabled option: '--users' (#32126)

Fixes #31515

Signed-off-by: Martin Bartoš <mabartos@redhat.com>
This commit is contained in:
Martin Bartoš 2024-08-15 08:23:17 +01:00 committed by GitHub
parent 3fda9f447d
commit 94fb762f8e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 2 deletions

View file

@ -29,9 +29,9 @@ import io.smallrye.config.SmallRyeConfig;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.keycloak.config.Option;
import org.keycloak.quarkus.runtime.Environment;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
import org.keycloak.utils.StringUtil;
import static org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider.NS_KEYCLOAK_PREFIX;
@ -56,6 +56,12 @@ public final class Configuration {
return getOptionalBooleanValue(propertyName).orElse(false);
}
public static boolean isBlank(Option<String> option) {
return getOptionalKcValue(option.getKey())
.map(StringUtil::isBlank)
.orElse(true);
}
public static boolean contains(Option<?> option, String value) {
return getOptionalValue(NS_KEYCLOAK_PREFIX + option.getKey())
.filter(f -> f.contains(value))

View file

@ -23,6 +23,7 @@ import org.keycloak.config.ExportOptions;
import org.keycloak.config.Option;
import org.keycloak.config.OptionBuilder;
import org.keycloak.config.OptionCategory;
import org.keycloak.exportimport.UsersExportStrategy;
import org.keycloak.quarkus.runtime.cli.PropertyException;
import org.keycloak.quarkus.runtime.configuration.Configuration;
@ -30,6 +31,7 @@ import java.util.Optional;
import static org.keycloak.exportimport.ExportImportConfig.PROVIDER;
import static org.keycloak.quarkus.runtime.configuration.Configuration.getOptionalValue;
import static org.keycloak.quarkus.runtime.configuration.Configuration.isBlank;
import static org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper.fromOption;
public final class ExportPropertyMappers {
@ -67,7 +69,7 @@ public final class ExportPropertyMappers {
.build(),
fromOption(ExportOptions.USERS)
.to("kc.spi-export-dir-users-export-strategy")
.isEnabled(ExportPropertyMappers::isDirProvider)
.validator(ExportPropertyMappers::validateUsersUsage)
.paramLabel("strategy")
.build(),
fromOption(ExportOptions.USERS_PER_FILE)
@ -78,6 +80,18 @@ public final class ExportPropertyMappers {
};
}
private static void validateUsersUsage(PropertyMapper<?> mapper, ConfigValue value) {
mapper.validateExpectedValues(value, mapper::validateSingleValue);
if (!isBlank(ExportOptions.FILE) && isBlank(ExportOptions.DIR)) {
var sameFileIsSpecified = UsersExportStrategy.SAME_FILE.toString().toLowerCase().equals(value.getValue());
if (!sameFileIsSpecified) {
throw new PropertyException("Property '--users' can be used only when exporting to a directory, or value set to 'same_file' when exporting to a file.");
}
}
}
public static void validateConfig() {
if (getOptionalValue(EXPORTER_PROPERTY).isEmpty() && System.getProperty(PROVIDER) == null) {
throw new PropertyException("Must specify either --dir or --file options.");

View file

@ -39,5 +39,16 @@ public class ExportDistTest {
cliResult = dist.run("export", "--realm=master");
cliResult.assertError("Must specify either --dir or --file options.");
cliResult = dist.run("export", "--file=master", "--users=skip");
cliResult.assertError("Property '--users' can be used only when exporting to a directory, or value set to 'same_file' when exporting to a file.");
cliResult = dist.run("export", "--file=some-file", "--users=same_file");
cliResult.assertNoError("Property '--users' can be used only when exporting to a directory, or value set to 'same_file' when exporting to a file.");
cliResult.assertMessage("Exporting model into file");
cliResult = dist.run("export", "--dir=some-dir", "--users=skip");
cliResult.assertMessage("Realm 'master' - data exported");
}
}

View file

@ -79,6 +79,11 @@ public interface CLIResult extends LaunchResult {
() -> "The Error Output:\n " + getErrorOutput() + "\ndoesn't contains " + msg);
}
default void assertNoError(String msg) {
assertFalse(getErrorOutput().contains(msg),
() -> "The Error Output:\n " + getErrorOutput() + "\n contains " + msg);
}
default void assertMessage(String message) {
assertThat(getOutput(), containsString(message));
}