Skip to content

Commit

Permalink
Add configuration to disable bootstrap of admin account (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
marioschlipf authored Nov 8, 2024
1 parent 6221e6d commit db1cb62
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 5 deletions.
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,17 @@ Use another Keycloak Docker image/version than used in this Testcontainer:
KeycloakContainer keycloak = new KeycloakContainer("quay.io/keycloak/keycloak:26.0");
```

### Initial admin user credentials

Use different admin credentials than the default internal (`admin`/`admin`) ones:

```java
@Container
KeycloakContainer keycloak = new KeycloakContainer()
.withAdminUsername("myKeycloakAdminUser")
.withAdminPassword("tops3cr3t");
```

### Realm Import

Power up a Keycloak instance with one or more existing realm JSON config files (from classpath):
Expand All @@ -56,13 +67,24 @@ or
.withRealmImportFiles("/test-realm-1.json", "/test-realm-2.json");
```

### Initial admin user credentials
If your realm JSON configuration file includes user definitions - particularly the admin user
for the master realm - ensure you disable the automatic bootstrapping of the admin user:

```java
@Container
KeycloakContainer keycloak = new KeycloakContainer()
.withBootstrapAdminDisabled()
.withRealmImportFile("/test-realm.json");
```

Use different admin credentials than the defaut internal (`admin`/`admin`) ones:
To retrieve a working Keycloak Admin Client from the container, make sure to override the admin
credentials to match those in your imported realm JSON configuration file:

```java
@Container
KeycloakContainer keycloak = new KeycloakContainer()
.withBootstrapAdminDisabled()
.withRealmImportFile("/test-realm.json")
.withAdminUsername("myKeycloakAdminUser")
.withAdminPassword("tops3cr3t");
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,8 @@ public abstract class ExtendableKeycloakContainer<SELF extends ExtendableKeycloa
private List<File> providerLibsLocations;
private List<String> customCommandParts;

private boolean bootstrapAdmin = true;

/**
* Create a KeycloakContainer with default image and version tag
*/
Expand Down Expand Up @@ -164,8 +166,11 @@ protected void configure() {
withEnv("KC_FEATURES_DISABLED", String.join(",", featuresDisabled));
}

withEnv("KC_BOOTSTRAP_ADMIN_USERNAME", adminUsername);
withEnv("KC_BOOTSTRAP_ADMIN_PASSWORD", adminPassword);
if (bootstrapAdmin) {
withEnv("KC_BOOTSTRAP_ADMIN_USERNAME", adminUsername);
withEnv("KC_BOOTSTRAP_ADMIN_PASSWORD", adminPassword);
}

withEnv("JAVA_OPTS_KC_HEAP", "-XX:InitialRAMPercentage=%d -XX:MaxRAMPercentage=%d".formatted(initialRamPercentage, maxRamPercentage));

if (useTls && isNotBlank(tlsCertificateFilename)) {
Expand Down Expand Up @@ -515,6 +520,16 @@ private SELF withDebug(int hostPort, boolean suspend) {
return self();
}

/** Disable default bootstrapping of the keycloak admin. Useful when realms are imported. */
public SELF withBootstrapAdminDisabled() {
this.bootstrapAdmin = false;
return self();
}

/**
* Returns the keycloak admin. Note that this may not return a functioning admin client
* if the master realm including users were imported.
*/
public Keycloak getKeycloakAdminClient() {
if (useTls) {
return Keycloak.getInstance(getAuthServerUrl(), MASTER_REALM, getAdminUsername(), getAdminPassword(), ADMIN_CLI_CLIENT, buildSslContext());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dasniko.testcontainers.keycloak;

import io.restassured.response.ValidatableResponse;
import jakarta.ws.rs.NotAuthorizedException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
Expand All @@ -23,6 +24,7 @@
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

Expand All @@ -32,6 +34,7 @@
public class KeycloakContainerTest {

public static final String TEST_REALM_JSON = "/test-realm.json";
public static final String MASTER_REALM_WITH_ADMIN_USER_JSON = "/master-realm-with-admin-user.json";

@Test
public void shouldStartKeycloak() {
Expand All @@ -49,7 +52,7 @@ public void shouldConsiderConfiguredStartupTimeout() {
try (KeycloakContainer keycloak = new KeycloakContainer().withStartupTimeout(duration)) {
keycloak.start();
}
} catch(ContainerLaunchException ex) {
} catch (ContainerLaunchException ex) {
Duration observedDuration = Duration.between(start, Instant.now());
assertTrue(observedDuration.toSeconds() >= MAX_TIMEOUT && observedDuration.toSeconds() < 30,
String.format("Startup time should consider configured limit of %d seconds, but took %d seconds",
Expand Down Expand Up @@ -91,6 +94,22 @@ public void shouldImportMultipleRealms() {
}
}

@Test
public void shouldImportMasterRealmAdmin() {
try (KeycloakContainer keycloak = new KeycloakContainer()
.withBootstrapAdminDisabled()
.withRealmImportFiles(MASTER_REALM_WITH_ADMIN_USER_JSON)) {
keycloak.start();

// Throws because we have imported a different admin user with different password
assertThrows(NotAuthorizedException.class, () -> keycloak.getKeycloakAdminClient().tokenManager().getAccessToken());

// Set password from imported realm, see json file
keycloak.withAdminPassword("password");
keycloak.getKeycloakAdminClient().tokenManager().getAccessToken();
}
}

@Test
public void shouldReturnServerInfo() {
try (KeycloakContainer keycloak = new KeycloakContainer()) {
Expand Down
23 changes: 23 additions & 0 deletions src/test/resources/master-realm-with-admin-user.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"realm": "master",
"enabled": true,
"users": [
{
"username": "admin",
"firstName": "Example",
"lastName": "User",
"email": "[email protected]",
"enabled": true,
"credentials": [
{
"type": "password",
"value": "password"
}
],
"realmRoles": [
"admin",
"default-roles-master"
]
}
]
}

0 comments on commit db1cb62

Please sign in to comment.