fix: narrow fix for creating single file import without a system prop (#36457)

closes: #34270

Signed-off-by: Steve Hawkins <shawkins@redhat.com>
This commit is contained in:
Steven Hawkins 2025-02-05 09:13:53 -05:00 committed by GitHub
parent 1cb7a4736c
commit f52cc73548
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 55 additions and 67 deletions

View File

@ -28,6 +28,7 @@ import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderConfigurationBuilder;
import java.util.List;
import java.util.Map;
import static org.keycloak.exportimport.ExportImportConfig.DEFAULT_STRATEGY;
@ -45,10 +46,10 @@ public class DirImportProviderFactory implements ImportProviderFactory {
private Config.Scope config;
@Override
public ImportProvider create(KeycloakSession session) {
public ImportProvider create(KeycloakSession session, Map<String, String> overrides) {
Strategy strategy = Enum.valueOf(Strategy.class, System.getProperty(ExportImportConfig.STRATEGY, config.get(STRATEGY, DEFAULT_STRATEGY.toString())));
String realmName = System.getProperty(ExportImportConfig.REALM_NAME, config.get(REALM_NAME));
String dir = System.getProperty(ExportImportConfig.DIR, config.get(DIR));
String dir = overrides.getOrDefault(ExportImportConfig.DIR, System.getProperty(ExportImportConfig.DIR, config.get(DIR)));
return new DirImportProvider(session.getKeycloakSessionFactory(), strategy)
.withDir(dir)
.withRealmName(realmName);
@ -73,6 +74,7 @@ public class DirImportProviderFactory implements ImportProviderFactory {
return PROVIDER_ID;
}
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()

View File

@ -29,6 +29,7 @@ import org.keycloak.provider.ProviderConfigurationBuilder;
import java.io.File;
import java.util.List;
import java.util.Map;
import static org.keycloak.exportimport.ExportImportConfig.DEFAULT_STRATEGY;
@ -47,9 +48,9 @@ public class SingleFileImportProviderFactory implements ImportProviderFactory {
private Config.Scope config;
@Override
public ImportProvider create(KeycloakSession session) {
public ImportProvider create(KeycloakSession session, Map<String, String> overrides) {
Strategy strategy = Enum.valueOf(Strategy.class, System.getProperty(ExportImportConfig.STRATEGY, config.get(STRATEGY, DEFAULT_STRATEGY.toString())));
String fileName = System.getProperty(ExportImportConfig.FILE, config.get(FILE));
String fileName = overrides.getOrDefault(ExportImportConfig.FILE, System.getProperty(ExportImportConfig.FILE, config.get(FILE)));
if (fileName == null) {
throw new IllegalArgumentException("Property " + FILE + " needs to be provided!");
}
@ -75,6 +76,7 @@ public class SingleFileImportProviderFactory implements ImportProviderFactory {
return PROVIDER_ID;
}
@Override
public List<ProviderConfigProperty> getConfigMetadata() {
return ProviderConfigurationBuilder.create()
.property()

View File

@ -17,10 +17,21 @@
package org.keycloak.exportimport;
import java.util.Map;
import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.ProviderFactory;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface ImportProviderFactory extends ProviderFactory<ImportProvider> {
ImportProvider create(KeycloakSession session, Map<String, String> overrides);
@Override
default ImportProvider create(KeycloakSession session) {
return create(session, Map.of());
}
}

View File

@ -29,9 +29,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Stream;
import static org.keycloak.exportimport.ExportImportConfig.PROVIDER;
@ -48,7 +49,7 @@ public class ExportImportManager {
private final KeycloakSession session;
private ExportProvider exportProvider;
private ImportProvider importProvider;
private List<ImportProvider> importProviders = List.of();
public ExportImportManager(KeycloakSession session) {
this.sessionFactory = session.getKeycloakSessionFactory();
@ -69,34 +70,22 @@ public class ExportImportManager {
}
} else if (ExportImportConfig.ACTION_IMPORT.equals(exportImportAction)) {
String providerId = System.getProperty(PROVIDER, Config.scope("import").get("importer", PROVIDER_DEFAULT));
importProvider = session.getProvider(ImportProvider.class, providerId);
ImportProvider importProvider = session.getProvider(ImportProvider.class, providerId);
if (importProvider == null) {
throw new RuntimeException("Import provider '" + providerId + "' not found");
}
importProviders = List.of(importProvider);
} else if (ExportImportConfig.getDir().isPresent()) { // import at startup
ExportImportConfig.setStrategy(Strategy.IGNORE_EXISTING);
ExportImportConfig.setReplacePlaceholders(true);
// enables logging of what is imported
ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
importProviders = getStartupImportProviders();
}
}
public boolean isImportMasterIncluded() {
if (importProvider != null) {
try {
return importProvider.isMasterRealmExported();
} catch (IOException e) {
throw new RuntimeException(e);
}
} else {
return isImportMasterIncludedAtStartup();
}
}
boolean isImportMasterIncludedAtStartup() {
return getStartupImportProviders().map(Supplier::get)
.anyMatch(provider -> {
return importProviders.stream().anyMatch(provider -> {
try {
return provider.isMasterRealmExported();
} catch (IOException e) {
@ -110,19 +99,7 @@ public class ExportImportManager {
}
public void runImport() {
if (importProvider != null) {
try {
importProvider.importModel();
} catch (IOException e) {
throw new RuntimeException("Failed to run import", e);
}
} else {
runImportAtStartup();
}
}
public void runImportAtStartup() {
getStartupImportProviders().map(Supplier::get).forEach(ip -> {
importProviders.forEach(ip -> {
try {
ip.importModel();
} catch (IOException e) {
@ -131,21 +108,20 @@ public class ExportImportManager {
});
}
private Stream<Supplier<ImportProvider>> getStartupImportProviders() {
private List<ImportProvider> getStartupImportProviders() {
var dirProp = ExportImportConfig.getDir();
if (dirProp.isEmpty()) {
return Stream.empty();
return List.of();
}
String dir = dirProp.get();
Stream<ProviderFactory> factories = sessionFactory.getProviderFactoriesStream(ImportProvider.class);
return factories.flatMap(factory -> {
String providerId = factory.getId();
if ("dir".equals(providerId)) {
Supplier<ImportProvider> func = () -> session.getProvider(ImportProvider.class, providerId);
return Stream.of(func);
return Stream.of(session.getProvider(ImportProvider.class, providerId));
}
if ("singleFile".equals(providerId)) {
Set<String> filesToImport = new HashSet<>();
@ -168,23 +144,13 @@ public class ExportImportManager {
filesToImport.add(file.getAbsolutePath());
}
return filesToImport.stream().map(file -> () -> {
// we need a new session to pickup the static system property
// file setting - it is picked up by the provider only at create time
// this will eventually need to be consolidated with the master existance check
// to prevent double parsing
KeycloakSession newSession = session.getKeycloakSessionFactory().create();
try {
ExportImportConfig.setFile(file);
return newSession.getProvider(ImportProvider.class, providerId);
} finally {
newSession.close();
}
});
if (factory instanceof ImportProviderFactory) {
return filesToImport.stream().map(file -> ((ImportProviderFactory)factory).create(session, Map.of(ExportImportConfig.FILE, file)));
}
}
return Stream.empty();
});
}).toList();
}
public void runExport() {

View File

@ -13,30 +13,37 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.provider.Provider;
import org.keycloak.services.DefaultKeycloakContext;
import org.keycloak.services.DefaultKeycloakSession;
import org.keycloak.services.DefaultKeycloakSessionFactory;
public class ExportImportManagerTest {
@After
public void reset() {
ExportImportConfig.reset();
}
@Test
public void testImportOnStartup() {
ExportImportConfig.setDir("/some/dir");
new ExportImportManager(new DefaultKeycloakSession(null) {
new ExportImportManager(new DefaultKeycloakSession(new DefaultKeycloakSessionFactory() {
@Override
public KeycloakSession create() {
return null;
}
}) {
@Override
protected DefaultKeycloakContext createKeycloakContext(KeycloakSession session) {
return null;
}
});
assertEquals(ExportImportConfig.ACTION_IMPORT, ExportImportConfig.getAction());
assertEquals(Strategy.IGNORE_EXISTING.toString(), ExportImportConfig.getStrategy());
assertTrue(ExportImportConfig.isReplacePlaceholders());
}
@Test
public void testImport() {
ExportImportConfig.setAction(ExportImportConfig.ACTION_IMPORT);
@ -46,28 +53,28 @@ public class ExportImportManagerTest {
protected DefaultKeycloakContext createKeycloakContext(KeycloakSession session) {
return null;
}
@Override
public <T extends Provider> T getProvider(Class<T> clazz, String id) {
return (T) new ImportProvider() {
@Override
public void close() {
}
@Override
public boolean isMasterRealmExported() throws IOException {
return false;
}
@Override
public void importModel() throws IOException {
}
};
}
});
assertEquals(ExportImportConfig.ACTION_IMPORT, ExportImportConfig.getAction());
assertNull(ExportImportConfig.getStrategy());