Replace createRealm/createClient and invalid use of ref with attachTo (#36640)

Closes #35995

Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
Stian Thorgersen 2025-01-21 14:05:14 +01:00 committed by GitHub
parent 14cf2233c5
commit 99e2f1df94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 120 additions and 38 deletions

View File

@ -21,5 +21,12 @@ public @interface InjectClient {
String realmRef() default "";
boolean createClient() default true;
/**
* Attach to an existing client instead of creating one; when attaching to an existing client the config will be ignored
* and the client will not be deleted automatically.
*
* @return the client-id of the existing client to attach to
*/
String attachTo() default "";
}

View File

@ -19,6 +19,12 @@ public @interface InjectRealm {
String ref() default "";
boolean createRealm() default true;
/**
* Attach to an existing realm instead of creating one; when attaching to an existing realm the config will be ignored
* and the realm will not be deleted automatically.
*
* @return the name of the existing realm to attach to
*/
String attachTo() default "";
}

View File

@ -29,28 +29,34 @@ public class ClientSupplier implements Supplier<ManagedClient, InjectClient> {
public ManagedClient getValue(InstanceContext<ManagedClient, InjectClient> instanceContext) {
ManagedRealm realm = instanceContext.getDependency(ManagedRealm.class, instanceContext.getAnnotation().realmRef());
ClientConfig config = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
ClientRepresentation clientRepresentation = config.configure(ClientConfigBuilder.create()).build();
String attachTo = instanceContext.getAnnotation().attachTo();
boolean managed = attachTo.isEmpty();
if (clientRepresentation.getClientId() == null) {
String clientId = SupplierHelpers.createName(instanceContext);
clientRepresentation.setClientId(clientId);
}
ClientRepresentation clientRepresentation;
if (managed) {
ClientConfig config = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
clientRepresentation = config.configure(ClientConfigBuilder.create()).build();
if (clientRepresentation.getClientId() == null) {
clientRepresentation.setClientId(SupplierHelpers.createName(instanceContext));
}
if (instanceContext.getAnnotation().createClient()) {
Response response = realm.admin().clients().create(clientRepresentation);
if (Status.CONFLICT.equals(Status.fromStatusCode(response.getStatus()))) {
throw new IllegalStateException("Client already exist with client id: " + clientRepresentation.getClientId());
}
clientRepresentation.setId(ApiUtil.handleCreatedResponse(response));
} else {
List<ClientRepresentation> clients = realm.admin().clients().findByClientId(clientRepresentation.getClientId());
List<ClientRepresentation> clients = realm.admin().clients().findByClientId(attachTo);
if (clients.isEmpty()) {
throw new IllegalStateException("No client found with client id: " + clientRepresentation.getClientId());
throw new IllegalStateException("No client found with client id: " + attachTo);
}
clientRepresentation = clients.get(0);
}
instanceContext.addNote("managed", managed);
ClientResource clientResource = realm.admin().clients().get(clientRepresentation.getId());
return new ManagedClient(clientRepresentation, clientResource);
}
@ -62,7 +68,9 @@ public class ClientSupplier implements Supplier<ManagedClient, InjectClient> {
@Override
public void close(InstanceContext<ManagedClient, InjectClient> instanceContext) {
instanceContext.getValue().admin().remove();
if (instanceContext.getNote("managed", Boolean.class)) {
instanceContext.getValue().admin().remove();
}
}
}

View File

@ -15,8 +15,6 @@ import org.keycloak.testframework.server.KeycloakServer;
public class RealmSupplier implements Supplier<ManagedRealm, InjectRealm> {
private static final String REALM_NAME_KEY = "realmName";
@Override
public Class<InjectRealm> getAnnotationClass() {
return InjectRealm.class;
@ -32,36 +30,40 @@ public class RealmSupplier implements Supplier<ManagedRealm, InjectRealm> {
KeycloakServer server = instanceContext.getDependency(KeycloakServer.class);
Keycloak adminClient = instanceContext.getDependency(Keycloak.class, "bootstrap-client");
RealmConfig config = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
String attachTo = instanceContext.getAnnotation().attachTo();
boolean managed = attachTo.isEmpty();
RealmConfigBuilder realmConfigBuilder = config.configure(RealmConfigBuilder.create());
RealmRepresentation realmRepresentation;
RealmConfigInterceptorHelper interceptor = new RealmConfigInterceptorHelper(instanceContext.getRegistry());
realmConfigBuilder = interceptor.intercept(realmConfigBuilder, instanceContext);
if (managed) {
RealmConfig config = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
RealmConfigBuilder realmConfigBuilder = config.configure(RealmConfigBuilder.create());
RealmRepresentation realmRepresentation = realmConfigBuilder.build();
RealmConfigInterceptorHelper interceptor = new RealmConfigInterceptorHelper(instanceContext.getRegistry());
realmConfigBuilder = interceptor.intercept(realmConfigBuilder, instanceContext);
if (realmRepresentation.getRealm() == null) {
String realmName = SupplierHelpers.createName(instanceContext);
realmRepresentation.setRealm(realmName);
}
realmRepresentation = realmConfigBuilder.build();
if (realmRepresentation.getId() == null) {
realmRepresentation.setId(realmRepresentation.getRealm());
}
if (realmRepresentation.getRealm() == null) {
realmRepresentation.setRealm(SupplierHelpers.createName(instanceContext));
}
String realmName = realmRepresentation.getRealm();
instanceContext.addNote(REALM_NAME_KEY, realmName);
if (realmRepresentation.getId() == null) {
realmRepresentation.setId(realmRepresentation.getRealm());
}
if (instanceContext.getAnnotation().createRealm()) {
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());
} else {
realmRepresentation = adminClient.realm(attachTo).toRepresentation();
}
// 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());
instanceContext.addNote("managed", managed);
RealmResource realmResource = adminClient.realm(realmRepresentation.getRealm());
return new ManagedRealm(server.getBaseUrl() + "/realms/" + realmName, realmRepresentation, realmResource);
return new ManagedRealm(server.getBaseUrl() + "/realms/" + realmRepresentation.getRealm(), realmRepresentation, realmResource);
}
@Override
@ -76,7 +78,7 @@ public class RealmSupplier implements Supplier<ManagedRealm, InjectRealm> {
@Override
public void close(InstanceContext<ManagedRealm, InjectRealm> instanceContext) {
if (instanceContext.getAnnotation().createRealm()) {
if (instanceContext.getNote("managed", Boolean.class)) {
instanceContext.getValue().admin().remove();
}
}

View File

@ -0,0 +1,59 @@
package org.keycloak.test.examples;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.testframework.annotations.InjectAdminClient;
import org.keycloak.testframework.annotations.InjectClient;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.realm.ManagedClient;
import org.keycloak.testframework.realm.ManagedRealm;
@KeycloakIntegrationTest
@TestMethodOrder(MethodOrderer.MethodName.class)
public class AttachToTest {
@InjectAdminClient
Keycloak adminClient;
@InjectRealm(attachTo = "master")
ManagedRealm attachedRealm;
@InjectClient
ManagedClient managedClient;
@InjectClient(ref = "admin-cli", attachTo = "admin-cli")
ManagedClient attachedClient;
@Test
public void aAttachedRealm() {
Assertions.assertEquals("master", attachedRealm.getName());
Assertions.assertEquals("master", attachedRealm.admin().toRepresentation().getRealm());
attachedRealm.updateWithCleanup(r -> r.editUsernameAllowed(true));
}
@Test
public void bRealmCleanup() {
Assertions.assertFalse(attachedRealm.admin().toRepresentation().isEditUsernameAllowed());
}
@Test
public void cManagedClient() {
Assertions.assertEquals("default", adminClient.realm("master").clients().get(managedClient.getId()).toRepresentation().getClientId());
}
@Test
public void dAttachedClient() {
Assertions.assertEquals("admin-cli", attachedClient.getClientId());
Assertions.assertEquals("admin-cli", attachedClient.admin().toRepresentation().getClientId());
}
@Test
public void eManagedClient() {
Assertions.assertEquals("default", managedClient.getClientId());
Assertions.assertEquals("default", managedClient.admin().toRepresentation().getClientId());
}
}

View File

@ -19,7 +19,7 @@ public class AdminSignatureAlgorithmTest {
@InjectAdminClient
Keycloak admin;
@InjectRealm(ref = "master", createRealm = false)
@InjectRealm(attachTo = "master")
ManagedRealm masterRealm;
@Test

View File

@ -39,7 +39,7 @@ public abstract class AbstractPermissionTest {
@InjectRealm(config = RealmAdminPermissionsConfig.class)
ManagedRealm realm;
@InjectClient(ref = Constants.ADMIN_PERMISSIONS_CLIENT_ID, createClient = false)
@InjectClient(attachTo = Constants.ADMIN_PERMISSIONS_CLIENT_ID)
ManagedClient client;
protected PermissionsResource getPermissionsResource() {

View File

@ -28,7 +28,7 @@ import org.keycloak.testframework.realm.ManagedClient;
@KeycloakIntegrationTest
public class FeatureDisabledTest {
@InjectClient(ref = "test-client", config = AuthzClientConfig.class, createClient = true)
@InjectClient(config = AuthzClientConfig.class)
private ManagedClient testClient;
@Test

View File

@ -28,7 +28,7 @@ import org.keycloak.testframework.realm.ManagedClient;
@KeycloakIntegrationTest(config = KeycloakAdminPermissionsV1ServerConfig.class)
public class FeatureV1EnabledTest {
@InjectClient(ref = "test-client", config = AuthzClientConfig.class, createClient = true)
@InjectClient(config = AuthzClientConfig.class)
private ManagedClient testClient;
@Test

View File

@ -40,7 +40,7 @@ public class FeatureV2EnabledTest {
@InjectRealm
private ManagedRealm realm;
@InjectClient(ref = "test-client", config = AuthzClientConfig.class, createClient = true)
@InjectClient(config = AuthzClientConfig.class)
private ManagedClient testClient;
@Test