mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-09 23:12:06 -03:30
Support injecting admin client configured from managed realm (#35693)
Closes #35501 Signed-off-by: stianst <stianst@gmail.com>
This commit is contained in:
parent
e05bd40d92
commit
d3b759b56f
@ -0,0 +1,9 @@
|
||||
package org.keycloak.test.framework;
|
||||
|
||||
public class TestFrameworkException extends RuntimeException {
|
||||
|
||||
public TestFrameworkException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,12 +3,18 @@ package org.keycloak.test.framework.admin;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.KeycloakBuilder;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.test.framework.TestFrameworkException;
|
||||
import org.keycloak.test.framework.annotations.InjectAdminClient;
|
||||
import org.keycloak.test.framework.config.Config;
|
||||
import org.keycloak.test.framework.injection.InstanceContext;
|
||||
import org.keycloak.test.framework.injection.LifeCycle;
|
||||
import org.keycloak.test.framework.injection.RequestedInstance;
|
||||
import org.keycloak.test.framework.injection.Supplier;
|
||||
import org.keycloak.test.framework.realm.ManagedRealm;
|
||||
import org.keycloak.test.framework.realm.ManagedUser;
|
||||
import org.keycloak.test.framework.server.KeycloakServer;
|
||||
|
||||
public class KeycloakAdminClientSupplier implements Supplier<Keycloak, InjectAdminClient> {
|
||||
@ -25,14 +31,46 @@ public class KeycloakAdminClientSupplier implements Supplier<Keycloak, InjectAdm
|
||||
|
||||
@Override
|
||||
public Keycloak getValue(InstanceContext<Keycloak, InjectAdminClient> instanceContext) {
|
||||
InjectAdminClient annotation = instanceContext.getAnnotation();
|
||||
|
||||
InjectAdminClient.Mode mode = annotation.mode();
|
||||
|
||||
KeycloakServer server = instanceContext.getDependency(KeycloakServer.class);
|
||||
return KeycloakBuilder.builder()
|
||||
KeycloakBuilder clientBuilder = KeycloakBuilder.builder()
|
||||
.serverUrl(server.getBaseUrl())
|
||||
.realm("master")
|
||||
.grantType(OAuth2Constants.CLIENT_CREDENTIALS)
|
||||
.clientId(Config.getAdminClientId())
|
||||
.clientSecret(Config.getAdminClientSecret())
|
||||
.build();
|
||||
.grantType(OAuth2Constants.CLIENT_CREDENTIALS);
|
||||
|
||||
if (mode.equals(InjectAdminClient.Mode.BOOTSTRAP)) {
|
||||
clientBuilder.realm("master").clientId(Config.getAdminClientId()).clientSecret(Config.getAdminClientSecret());
|
||||
} else if (mode.equals(InjectAdminClient.Mode.MANAGED_REALM)) {
|
||||
ManagedRealm managedRealm = instanceContext.getDependency(ManagedRealm.class);
|
||||
clientBuilder.realm(managedRealm.getName());
|
||||
|
||||
String clientId = !annotation.client().isEmpty() ? annotation.client() : null;
|
||||
String userId = !annotation.user().isEmpty() ? annotation.user() : null;
|
||||
|
||||
if (clientId == null) {
|
||||
throw new TestFrameworkException("Client is required when using managed realm mode");
|
||||
}
|
||||
|
||||
RealmRepresentation realmRep = managedRealm.getCreatedRepresentation();
|
||||
ClientRepresentation clientRep = realmRep.getClients().stream()
|
||||
.filter(c -> c.getClientId().equals(annotation.client()))
|
||||
.findFirst().orElseThrow(() -> new TestFrameworkException("Client " + annotation.client() + " not found in managed realm"));
|
||||
|
||||
clientBuilder.clientId(clientId).clientSecret(clientRep.getSecret());
|
||||
|
||||
if (userId != null) {
|
||||
UserRepresentation userRep = realmRep.getUsers().stream()
|
||||
.filter(u -> u.getUsername().equals(annotation.user()))
|
||||
.findFirst().orElseThrow(() -> new TestFrameworkException("User " + annotation.user() + " not found in managed realm"));
|
||||
String password = ManagedUser.getPassword(userRep);
|
||||
clientBuilder.username(userRep.getUsername()).password(password);
|
||||
clientBuilder.grantType(OAuth2Constants.PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
return clientBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -10,4 +10,19 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface InjectAdminClient {
|
||||
|
||||
String ref() default "";
|
||||
|
||||
Mode mode() default Mode.BOOTSTRAP;
|
||||
|
||||
String client() default "";
|
||||
|
||||
String user() default "";
|
||||
|
||||
enum Mode {
|
||||
|
||||
BOOTSTRAP,
|
||||
MANAGED_REALM
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -7,19 +7,23 @@ import java.lang.reflect.Proxy;
|
||||
public class DefaultAnnotationProxy implements InvocationHandler {
|
||||
|
||||
private final Class<?> annotationClass;
|
||||
private final String ref;
|
||||
|
||||
public <S> DefaultAnnotationProxy(Class<?> annotationClass) {
|
||||
public <S> DefaultAnnotationProxy(Class<?> annotationClass, String ref) {
|
||||
this.annotationClass = annotationClass;
|
||||
this.ref = ref;
|
||||
}
|
||||
|
||||
public static <S> S proxy(Class<S> annotationClass) {
|
||||
return (S) Proxy.newProxyInstance(DefaultAnnotationProxy.class.getClassLoader(), new Class<?>[]{annotationClass}, new DefaultAnnotationProxy(annotationClass));
|
||||
public static <S> S proxy(Class<S> annotationClass, String ref) {
|
||||
return (S) Proxy.newProxyInstance(DefaultAnnotationProxy.class.getClassLoader(), new Class<?>[]{annotationClass}, new DefaultAnnotationProxy(annotationClass, ref));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
if (method.getName().equals("annotationType")) {
|
||||
return annotationClass;
|
||||
} else if (method.getName().equals("ref")) {
|
||||
return ref;
|
||||
} else {
|
||||
return annotationClass.getMethod(method.getName()).getDefaultValue();
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ public class Registry implements ExtensionContext.Store.CloseableResource {
|
||||
private <T> T getUnConfiguredDependency(Class<T> typeClass, String ref, InstanceContext dependent) {
|
||||
InstanceContext dependency;
|
||||
Supplier<?, ?> supplier = extensions.findSupplierByType(typeClass);
|
||||
Annotation defaultAnnotation = DefaultAnnotationProxy.proxy(supplier.getAnnotationClass());
|
||||
Annotation defaultAnnotation = DefaultAnnotationProxy.proxy(supplier.getAnnotationClass(), ref);
|
||||
dependency = new InstanceContext(-1, this, supplier, defaultAnnotation, typeClass);
|
||||
|
||||
dependency.registerDependency(dependent);
|
||||
@ -238,7 +238,7 @@ public class Registry implements ExtensionContext.Store.CloseableResource {
|
||||
} else {
|
||||
Supplier<?, ?> supplier = extensions.findSupplierByType(valueType);
|
||||
if (supplier != null) {
|
||||
Annotation defaultAnnotation = DefaultAnnotationProxy.proxy(supplier.getAnnotationClass());
|
||||
Annotation defaultAnnotation = DefaultAnnotationProxy.proxy(supplier.getAnnotationClass(), "");
|
||||
return new RequestedInstance(supplier, defaultAnnotation, valueType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,6 +40,10 @@ public class ManagedRealm extends ManagedTestResource {
|
||||
return realmResource;
|
||||
}
|
||||
|
||||
public RealmRepresentation getCreatedRepresentation() {
|
||||
return createdRepresentation;
|
||||
}
|
||||
|
||||
public void updateWithCleanup(RealmUpdate... updates) {
|
||||
RealmRepresentation rep = admin().toRepresentation();
|
||||
cleanup().resetToOriginalRepresentation(rep);
|
||||
|
||||
@ -26,12 +26,16 @@ public class ManagedUser {
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
Optional<CredentialRepresentation> password = createdRepresentation.getCredentials().stream().filter(c -> c.getType().equals(CredentialRepresentation.PASSWORD)).findFirst();
|
||||
return password.map(CredentialRepresentation::getValue).orElse(null);
|
||||
return getPassword(createdRepresentation);
|
||||
}
|
||||
|
||||
public UserResource admin() {
|
||||
return userResource;
|
||||
}
|
||||
|
||||
public static String getPassword(UserRepresentation userRepresentation) {
|
||||
Optional<CredentialRepresentation> password = userRepresentation.getCredentials().stream().filter(c -> c.getType().equals(CredentialRepresentation.PASSWORD)).findFirst();
|
||||
return password.map(CredentialRepresentation::getValue).orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ public class RealmSupplier implements Supplier<ManagedRealm, InjectRealm> {
|
||||
@Override
|
||||
public ManagedRealm getValue(InstanceContext<ManagedRealm, InjectRealm> instanceContext) {
|
||||
KeycloakServer server = instanceContext.getDependency(KeycloakServer.class);
|
||||
Keycloak adminClient = instanceContext.getDependency(Keycloak.class);
|
||||
Keycloak adminClient = instanceContext.getDependency(Keycloak.class, "bootstrap-client");
|
||||
|
||||
RealmConfig config = SupplierHelpers.getInstance(instanceContext.getAnnotation().config());
|
||||
|
||||
|
||||
@ -16,16 +16,22 @@ public class DefaultAnnotationProxyTest {
|
||||
|
||||
@Test
|
||||
public void testGetField() {
|
||||
MockAnnotation proxy = DefaultAnnotationProxy.proxy(MockAnnotation.class);
|
||||
MockAnnotation proxy = DefaultAnnotationProxy.proxy(MockAnnotation.class, "");
|
||||
Assertions.assertEquals(LifeCycle.CLASS, proxy.lifecycle());
|
||||
Assertions.assertEquals(LinkedList.class, proxy.config());
|
||||
Assertions.assertEquals("", proxy.ref());
|
||||
Assertions.assertEquals("else", proxy.something());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomRef() {
|
||||
MockAnnotation proxy = DefaultAnnotationProxy.proxy(MockAnnotation.class, "myref");
|
||||
Assertions.assertEquals("myref", proxy.ref());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAnnotationReflection() {
|
||||
MockAnnotation proxy = DefaultAnnotationProxy.proxy(MockAnnotation.class);
|
||||
MockAnnotation proxy = DefaultAnnotationProxy.proxy(MockAnnotation.class, "");
|
||||
Assertions.assertEquals(LifeCycle.CLASS, SupplierHelpers.getAnnotationField(proxy, "lifecycle"));
|
||||
Assertions.assertEquals(LinkedList.class, SupplierHelpers.getAnnotationField(proxy, "config"));
|
||||
Assertions.assertEquals("", SupplierHelpers.getAnnotationField(proxy, "ref"));
|
||||
|
||||
@ -2,12 +2,17 @@ package org.keycloak.test.examples;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.admin.client.Keycloak;
|
||||
import org.keycloak.admin.client.resource.RealmResource;
|
||||
import org.keycloak.jose.jws.JWSInput;
|
||||
import org.keycloak.jose.jws.JWSInputException;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.MappingsRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.test.framework.annotations.InjectAdminClient;
|
||||
import org.keycloak.test.framework.annotations.InjectRealm;
|
||||
import org.keycloak.test.framework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.test.framework.realm.ManagedRealm;
|
||||
@ -17,14 +22,31 @@ import org.keycloak.test.framework.realm.RealmConfigBuilder;
|
||||
import java.util.List;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class RealmWithClientAndUserTest {
|
||||
public class RealmSpecificAdminClientTest {
|
||||
|
||||
@InjectRealm(config = RealmWithClientAndUser.class)
|
||||
ManagedRealm realm;
|
||||
|
||||
@InjectAdminClient(ref = "bootstrap-client")
|
||||
Keycloak bootstrapAdminClient;
|
||||
|
||||
@InjectAdminClient(mode = InjectAdminClient.Mode.MANAGED_REALM, client = "myclient", user = "myadmin")
|
||||
Keycloak realmAdminClient;
|
||||
|
||||
@Test
|
||||
public void testAdminClientIssuers() throws JWSInputException {
|
||||
AccessToken bootstrapAccessToken = new JWSInput(bootstrapAdminClient.tokenManager().getAccessToken().getToken()).readJsonContent(AccessToken.class);
|
||||
Assertions.assertTrue(bootstrapAccessToken.getIssuer().endsWith("/realms/master"));
|
||||
|
||||
AccessToken realmAccessToken = new JWSInput(realmAdminClient.tokenManager().getAccessToken().getToken()).readJsonContent(AccessToken.class);
|
||||
Assertions.assertTrue(realmAccessToken.getIssuer().endsWith("/realms/" + realm.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRealmWithClientAndUser() {
|
||||
List<ClientRepresentation> clients = realm.admin().clients().findByClientId("myclient");
|
||||
RealmResource realmResource = realmAdminClient.realms().realm(realm.getName());
|
||||
|
||||
List<ClientRepresentation> clients = realmResource.clients().findByClientId("myclient");
|
||||
Assertions.assertEquals(1, clients.size());
|
||||
|
||||
ClientRepresentation client = clients.get(0);
|
||||
@ -42,7 +64,7 @@ public class RealmWithClientAndUserTest {
|
||||
Assertions.assertEquals("myadmin@localhost", user.getEmail());
|
||||
Assertions.assertTrue(user.isEmailVerified());
|
||||
|
||||
MappingsRepresentation roles = realm.admin().users().get(user.getId()).roles().getAll();
|
||||
MappingsRepresentation roles = realmResource.users().get(user.getId()).roles().getAll();
|
||||
Assertions.assertEquals(1, roles.getClientMappings().get(Constants.REALM_MANAGEMENT_CLIENT_ID).getMappings().size());
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user