Testsuite PoC - Use service account for admin client (#31478)

Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
Stian Thorgersen 2024-07-24 13:14:50 +02:00 committed by GitHub
parent 9f2eddead8
commit b4368b75e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 74 additions and 108 deletions

View file

@ -46,9 +46,7 @@ import jakarta.ws.rs.ApplicationPath;
public class QuarkusKeycloakApplication extends KeycloakApplication { public class QuarkusKeycloakApplication extends KeycloakApplication {
private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN"; private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN";
private static final String KEYCLOAK_ADMIN_PROP_VAR = "keycloakAdmin";
private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD"; private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD";
private static final String KEYCLOAK_ADMIN_PASSWORD_PROP_VAR = "keycloakAdminPassword";
void onStartupEvent(@Observes StartupEvent event) { void onStartupEvent(@Observes StartupEvent event) {
QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform(); QuarkusPlatform platform = (QuarkusPlatform) Platform.getPlatform();
@ -79,8 +77,8 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
@Override @Override
protected void createTemporaryAdmin(KeycloakSession session) { protected void createTemporaryAdmin(KeycloakSession session) {
var adminUsername = Configuration.getOptionalKcValue(BootstrapAdminOptions.USERNAME.getKey()).orElse(getEnvOrProp(KEYCLOAK_ADMIN_ENV_VAR, KEYCLOAK_ADMIN_PROP_VAR)); var adminUsername = Configuration.getOptionalKcValue(BootstrapAdminOptions.USERNAME.getKey()).orElse(System.getenv(KEYCLOAK_ADMIN_ENV_VAR));
var adminPassword = Configuration.getOptionalKcValue(BootstrapAdminOptions.PASSWORD.getKey()).orElse(getEnvOrProp(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR, KEYCLOAK_ADMIN_PASSWORD_PROP_VAR)); var adminPassword = Configuration.getOptionalKcValue(BootstrapAdminOptions.PASSWORD.getKey()).orElse(System.getenv(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR));
var clientId = Configuration.getOptionalKcValue(BootstrapAdminOptions.CLIENT_ID.getKey()).orElse(null); var clientId = Configuration.getOptionalKcValue(BootstrapAdminOptions.CLIENT_ID.getKey()).orElse(null);
var clientSecret = Configuration.getOptionalKcValue(BootstrapAdminOptions.CLIENT_SECRET.getKey()).orElse(null); var clientSecret = Configuration.getOptionalKcValue(BootstrapAdminOptions.CLIENT_SECRET.getKey()).orElse(null);
@ -106,9 +104,4 @@ public class QuarkusKeycloakApplication extends KeycloakApplication {
new ApplianceBootstrap(session).createTemporaryMasterRealmAdminService(clientId, clientSecret /*, adminExpiration*/); new ApplianceBootstrap(session).createTemporaryMasterRealmAdminService(clientId, clientSecret /*, adminExpiration*/);
} }
private String getEnvOrProp(String envKey, String propKey) {
String value = System.getenv(envKey);
return value != null ? value : System.getProperty(propKey);
}
} }

View file

@ -1,19 +0,0 @@
package org.keycloak.test.examples;
import org.keycloak.test.framework.server.KeycloakTestServerConfig;
import java.util.Optional;
public class NoAdminUserKeycloakTestServerConfig implements KeycloakTestServerConfig {
@Override
public String adminUserName() {
return null;
}
@Override
public String adminUserPassword() {
return null;
}
}

View file

@ -1,17 +1,26 @@
package org.keycloak.test.examples; package org.keycloak.test.examples;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.test.framework.annotations.KeycloakIntegrationTest; import org.keycloak.test.framework.annotations.KeycloakIntegrationTest;
import org.keycloak.test.framework.annotations.TestAdminClient;
import org.keycloak.test.framework.page.WelcomePage; import org.keycloak.test.framework.page.WelcomePage;
import org.keycloak.test.framework.annotations.TestWebDriver; import org.keycloak.test.framework.annotations.TestWebDriver;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
@KeycloakIntegrationTest(config = NoAdminUserKeycloakTestServerConfig.class) import java.util.List;
@KeycloakIntegrationTest
public class WelcomePageTest { public class WelcomePageTest {
@TestWebDriver @TestWebDriver
WebDriver driver; WebDriver driver;
@TestAdminClient
Keycloak adminClient;
@Test @Test
public void testCreateUser() { public void testCreateUser() {
final var welcomePage = new WelcomePage(driver); final var welcomePage = new WelcomePage(driver);
@ -19,6 +28,11 @@ public class WelcomePageTest {
welcomePage.fillRegistration("admin", "admin"); welcomePage.fillRegistration("admin", "admin");
welcomePage.submit(); welcomePage.submit();
welcomePage.assertUserCreated(); welcomePage.assertUserCreated();
List<UserRepresentation> users = adminClient.realm("master").users().search("admin", true);
Assertions.assertEquals(1, users.size());
adminClient.realm("master").users().get(users.get(0).getId()).remove();
} }
} }

View file

@ -1,7 +1,10 @@
package org.keycloak.test.framework.admin; package org.keycloak.test.framework.admin;
import org.keycloak.OAuth2Constants;
import org.keycloak.admin.client.Keycloak; import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.KeycloakBuilder;
import org.keycloak.test.framework.annotations.TestAdminClient; import org.keycloak.test.framework.annotations.TestAdminClient;
import org.keycloak.test.framework.config.Config;
import org.keycloak.test.framework.injection.InstanceContext; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
import org.keycloak.test.framework.injection.RequestedInstance; import org.keycloak.test.framework.injection.RequestedInstance;
@ -23,7 +26,13 @@ public class KeycloakAdminClientSupplier implements Supplier<Keycloak, TestAdmin
@Override @Override
public Keycloak getValue(InstanceContext<Keycloak, TestAdminClient> instanceContext) { public Keycloak getValue(InstanceContext<Keycloak, TestAdminClient> instanceContext) {
KeycloakTestServer testServer = instanceContext.getDependency(KeycloakTestServer.class); KeycloakTestServer testServer = instanceContext.getDependency(KeycloakTestServer.class);
return Keycloak.getInstance(testServer.getBaseUrl(), "master", "admin", "admin", "admin-cli"); return KeycloakBuilder.builder()
.serverUrl(testServer.getBaseUrl())
.realm("master")
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
.clientId(Config.getAdminClientId())
.clientSecret(Config.getAdminClientSecret())
.build();
} }
@Override @Override

View file

@ -17,6 +17,14 @@ public class Config {
return config.getOptionalValue("kc.test." + ValueTypeAlias.getAlias(valueType), String.class).orElse(null); return config.getOptionalValue("kc.test." + ValueTypeAlias.getAlias(valueType), String.class).orElse(null);
} }
public static String getAdminClientId() {
return "kc-test-admin";
}
public static String getAdminClientSecret() {
return "kc-test-secret";
}
public static SmallRyeConfig initConfig() { public static SmallRyeConfig initConfig() {
SmallRyeConfigBuilder configBuilder = new SmallRyeConfigBuilder() SmallRyeConfigBuilder configBuilder = new SmallRyeConfigBuilder()
.addDefaultSources() .addDefaultSources()

View file

@ -41,6 +41,9 @@ public class RealmSupplier implements Supplier<RealmResource, TestRealm> {
adminClient.realms().create(realmRepresentation); adminClient.realms().create(realmRepresentation);
// TODO Token needs to be invalidated after creating realm to have roles for new realm in the token. Maybe lightweight access tokens could help.
adminClient.tokenManager().invalidate(adminClient.tokenManager().getAccessTokenString());
return adminClient.realm(realmRepresentation.getRealm()); return adminClient.realm(realmRepresentation.getRealm());
} }

View file

@ -1,6 +1,7 @@
package org.keycloak.test.framework.server; package org.keycloak.test.framework.server;
import org.keycloak.test.framework.annotations.KeycloakIntegrationTest; import org.keycloak.test.framework.annotations.KeycloakIntegrationTest;
import org.keycloak.test.framework.config.Config;
import org.keycloak.test.framework.database.TestDatabase; import org.keycloak.test.framework.database.TestDatabase;
import org.keycloak.test.framework.injection.InstanceContext; import org.keycloak.test.framework.injection.InstanceContext;
import org.keycloak.test.framework.injection.LifeCycle; import org.keycloak.test.framework.injection.LifeCycle;
@ -8,8 +9,8 @@ import org.keycloak.test.framework.injection.RequestedInstance;
import org.keycloak.test.framework.injection.Supplier; import org.keycloak.test.framework.injection.Supplier;
import org.keycloak.test.framework.injection.SupplierHelpers; import org.keycloak.test.framework.injection.SupplierHelpers;
import java.util.Collections; import java.util.LinkedList;
import java.util.Map; import java.util.List;
public abstract class AbstractKeycloakTestServerSupplier implements Supplier<KeycloakTestServer, KeycloakIntegrationTest> { public abstract class AbstractKeycloakTestServerSupplier implements Supplier<KeycloakTestServer, KeycloakIntegrationTest> {
@ -28,17 +29,27 @@ public abstract class AbstractKeycloakTestServerSupplier implements Supplier<Key
KeycloakIntegrationTest annotation = instanceContext.getAnnotation(); KeycloakIntegrationTest annotation = instanceContext.getAnnotation();
KeycloakTestServerConfig serverConfig = SupplierHelpers.getInstance(annotation.config()); KeycloakTestServerConfig serverConfig = SupplierHelpers.getInstance(annotation.config());
Map<String, String> databaseConfig; List<String> rawOptions = new LinkedList<>();
if (requiresDatabase()) { rawOptions.add("start-dev");
TestDatabase testDatabase = instanceContext.getDependency(TestDatabase.class); rawOptions.add("--cache=local");
databaseConfig = testDatabase.getServerConfig();
} else { rawOptions.add("--bootstrap-admin-client-id=" + Config.getAdminClientId());
databaseConfig = Collections.emptyMap(); rawOptions.add("--bootstrap-admin-client-secret=" + Config.getAdminClientSecret());
if (!serverConfig.features().isEmpty()) {
rawOptions.add("--features=" + String.join(",", serverConfig.features()));
} }
KeycloakTestServer keycloakTestServer = getServer(); serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
keycloakTestServer.start(serverConfig, databaseConfig);
return keycloakTestServer; if (requiresDatabase()) {
TestDatabase testDatabase = instanceContext.getDependency(TestDatabase.class);
testDatabase.getServerConfig().forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
}
KeycloakTestServer server = getServer();
server.start(rawOptions);
return server;
} }
@Override @Override

View file

@ -2,42 +2,22 @@ package org.keycloak.test.framework.server;
import org.keycloak.it.utils.RawKeycloakDistribution; import org.keycloak.it.utils.RawKeycloakDistribution;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
public class DistributionKeycloakTestServer implements KeycloakTestServer { public class DistributionKeycloakTestServer implements KeycloakTestServer {
private boolean debug = false; private static final boolean DEBUG = false;
private boolean manualStop = true; private static final boolean MANUAL_STOP = true;
private boolean enableTls = false; private static final boolean ENABLE_TLS = false;
private boolean reCreate = false; private static final boolean RE_CREATE = false;
private boolean removeBuildOptionsAfterBuild = false; private static final boolean REMOVE_BUILD_OPTIONS_AFTER_BUILD = false;
private int requestPort = 8080; private static final int REQUEST_PORT = 8080;
private RawKeycloakDistribution keycloak; private RawKeycloakDistribution keycloak;
@Override @Override
public void start(KeycloakTestServerConfig serverConfig, Map<String, String> databaseConfig) { public void start(List<String> rawOptions) {
keycloak = new RawKeycloakDistribution(debug, manualStop, enableTls, reCreate, removeBuildOptionsAfterBuild, requestPort); keycloak = new RawKeycloakDistribution(DEBUG, MANUAL_STOP, ENABLE_TLS, RE_CREATE, REMOVE_BUILD_OPTIONS_AFTER_BUILD, REQUEST_PORT);
// Set environment variables user and password for Keycloak Admin used by Keycloak instance.
keycloak.setEnvVar("KC_BOOTSTRAP_ADMIN_USERNAME", serverConfig.adminUserName());
keycloak.setEnvVar("KC_BOOTSTRAP_ADMIN_PASSWORD", serverConfig.adminUserPassword());
List<String> rawOptions = new LinkedList<>();
rawOptions.add("start-dev");
//rawOptions.add("--db=dev-mem"); // TODO With dev-mem there's an issue as the H2 DB isn't stopped when restarting embedded server
rawOptions.add("--cache=local");
if (!serverConfig.features().isEmpty()) {
rawOptions.add("--features=" + String.join(",", serverConfig.features()));
}
serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
databaseConfig.forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
keycloak.run(rawOptions).assertStartedDevMode(); keycloak.run(rawOptions).assertStartedDevMode();
} }

View file

@ -3,9 +3,7 @@ package org.keycloak.test.framework.server;
import org.keycloak.Keycloak; import org.keycloak.Keycloak;
import org.keycloak.common.Version; import org.keycloak.common.Version;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
public class EmbeddedKeycloakTestServer implements KeycloakTestServer { public class EmbeddedKeycloakTestServer implements KeycloakTestServer {
@ -13,29 +11,7 @@ public class EmbeddedKeycloakTestServer implements KeycloakTestServer {
private Keycloak keycloak; private Keycloak keycloak;
@Override @Override
public void start(KeycloakTestServerConfig serverConfig, Map<String, String> databaseConfig) { public void start(List<String> rawOptions) {
List<String> rawOptions = new LinkedList<>();
rawOptions.add("start-dev");
rawOptions.add("--cache=local");
if (!serverConfig.features().isEmpty()) {
rawOptions.add("--features=" + String.join(",", serverConfig.features()));
}
if (serverConfig.adminUserName() != null) {
System.setProperty("keycloakAdmin", serverConfig.adminUserName());
} else {
System.getProperties().remove("keycloakAdmin");
}
if (serverConfig.adminUserPassword() != null) {
System.setProperty("keycloakAdminPassword", serverConfig.adminUserPassword());
} else {
System.getProperties().remove("keycloakAdminPassword");
}
serverConfig.options().forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
databaseConfig.forEach((key, value) -> rawOptions.add("--" + key + "=" + value));
keycloak = Keycloak.builder() keycloak = Keycloak.builder()
.setVersion(Version.VERSION) .setVersion(Version.VERSION)
.start(rawOptions); .start(rawOptions);

View file

@ -1,10 +1,10 @@
package org.keycloak.test.framework.server; package org.keycloak.test.framework.server;
import java.util.Map; import java.util.List;
public interface KeycloakTestServer { public interface KeycloakTestServer {
void start(KeycloakTestServerConfig serverConfig, Map<String, String> databaseConfig); void start(List<String> rawOptions);
void stop(); void stop();

View file

@ -2,7 +2,6 @@ package org.keycloak.test.framework.server;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
public interface KeycloakTestServerConfig { public interface KeycloakTestServerConfig {
@ -15,12 +14,4 @@ public interface KeycloakTestServerConfig {
return Collections.emptySet(); return Collections.emptySet();
} }
default String adminUserName() {
return "admin";
}
default String adminUserPassword() {
return "admin";
}
} }

View file

@ -1,11 +1,11 @@
package org.keycloak.test.framework.server; package org.keycloak.test.framework.server;
import java.util.Map; import java.util.List;
public class RemoteKeycloakTestServer implements KeycloakTestServer { public class RemoteKeycloakTestServer implements KeycloakTestServer {
@Override @Override
public void start(KeycloakTestServerConfig serverConfig, Map<String, String> databaseConfig) { public void start(List<String> rawOptions) {
} }
@Override @Override