Move RealmTest.java to the new testsuite (#41326)

Part of: #34494

Signed-off-by: Lukas Hanusovsky <lhanusov@redhat.com>
This commit is contained in:
Lukas Hanusovsky 2025-08-11 16:24:27 +02:00 committed by GitHub
parent 811e153398
commit f12ab6b189
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 1454 additions and 1259 deletions

View File

@ -0,0 +1,30 @@
package org.keycloak.tests.admin.realm;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.testframework.admin.AdminClientFactory;
import org.keycloak.testframework.annotations.InjectAdminClient;
import org.keycloak.testframework.annotations.InjectAdminClientFactory;
import org.keycloak.testframework.annotations.InjectAdminEvents;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.events.AdminEvents;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
public class AbstractRealmTest {
@InjectRealm(ref = "managedRealm")
ManagedRealm managedRealm;
@InjectAdminClient(ref = "managed", realmRef = "managedRealm")
Keycloak adminClient;
@InjectAdminClientFactory
AdminClientFactory adminClientFactory;
@InjectRunOnServer(ref = "managed", realmRef = "managedRealm")
RunOnServerClient runOnServer;
@InjectAdminEvents(ref = "managedEvents", realmRef = "managedRealm")
AdminEvents adminEvents;
}

View File

@ -0,0 +1,270 @@
package org.keycloak.tests.admin.realm;
import com.google.common.collect.Sets;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.BrowserSecurityHeaders;
import org.keycloak.models.CibaConfig;
import org.keycloak.models.OAuth2DeviceConfig;
import org.keycloak.models.OTPPolicy;
import org.keycloak.models.ParConfig;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider;
import org.keycloak.models.cache.CacheRealmProvider;
import org.keycloak.models.cache.infinispan.RealmAdapter;
import org.keycloak.models.jpa.entities.RealmAttributes;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.events.AdminEventAssertion;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@KeycloakIntegrationTest
public class RealmAttributesTest extends AbstractRealmTest {
/**
* Checks attributes exposed as fields are not also included as attributes
*/
@Test
public void excludesFieldsFromAttributes() {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm("attributes");
adminClient.realms().create(rep);
RealmRepresentation rep2 = adminClient.realm("attributes").toRepresentation();
if (rep2.getAttributes() != null) {
Stream.of(CibaConfig.CIBA_BACKCHANNEL_TOKEN_DELIVERY_MODE,
CibaConfig.CIBA_EXPIRES_IN,
CibaConfig.CIBA_INTERVAL,
CibaConfig.CIBA_AUTH_REQUESTED_USER_HINT).forEach(i -> rep2.getAttributes().remove(i));
}
Set<String> attributesKeys = rep2.getAttributes().keySet();
int expectedAttributesCount = 3;
final Set<String> expectedAttributes = Sets.newHashSet(
OAuth2DeviceConfig.OAUTH2_DEVICE_CODE_LIFESPAN,
OAuth2DeviceConfig.OAUTH2_DEVICE_POLLING_INTERVAL,
ParConfig.PAR_REQUEST_URI_LIFESPAN
);
// This attribute is represented in Legacy store as attribute and for Map store as a field
expectedAttributes.add(OTPPolicy.REALM_REUSABLE_CODE_ATTRIBUTE);
expectedAttributesCount++;
assertThat(attributesKeys.size(), CoreMatchers.is(expectedAttributesCount));
assertThat(attributesKeys, CoreMatchers.is(expectedAttributes));
adminClient.realms().realm("attributes").remove();
}
/**
* Checks attributes exposed as fields are not deleted on update realm
*/
@Test
public void testFieldNotErased() {
Long dummyLong = 999L;
Integer dummyInt = 999;
Map<String, String> browserSecurityHeaders = new HashMap<>(Arrays.stream(
BrowserSecurityHeaders.values()).collect(Collectors.toMap(
BrowserSecurityHeaders::getKey,
headerValue -> headerValue.getDefaultValue().isBlank()
? "non-null-to-test"
: headerValue.getDefaultValue()
)));
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm("attributes");
rep.setDisplayName("DISPLAY_NAME");
rep.setDisplayNameHtml("DISPLAY_NAME_HTML");
rep.setDefaultSignatureAlgorithm("RS256");
rep.setBruteForceProtected(true);
rep.setPermanentLockout(true);
rep.setMaxFailureWaitSeconds(dummyInt);
rep.setWaitIncrementSeconds(dummyInt);
rep.setQuickLoginCheckMilliSeconds(dummyLong);
rep.setMinimumQuickLoginWaitSeconds(dummyInt);
rep.setMaxDeltaTimeSeconds(dummyInt);
rep.setFailureFactor(dummyInt);
rep.setActionTokenGeneratedByAdminLifespan(dummyInt);
rep.setActionTokenGeneratedByUserLifespan(dummyInt);
rep.setOfflineSessionMaxLifespanEnabled(true);
rep.setOfflineSessionMaxLifespan(dummyInt);
rep.setBrowserSecurityHeaders(browserSecurityHeaders);
rep.setWebAuthnPolicyRpEntityName("RP_ENTITY_NAME");
rep.setWebAuthnPolicySignatureAlgorithms(Collections.singletonList("RS256"));
rep.setWebAuthnPolicyRpId("localhost");
rep.setWebAuthnPolicyAttestationConveyancePreference("Direct");
rep.setWebAuthnPolicyAuthenticatorAttachment("Platform");
rep.setWebAuthnPolicyRequireResidentKey("Yes");
rep.setWebAuthnPolicyUserVerificationRequirement("Required");
rep.setWebAuthnPolicyCreateTimeout(dummyInt);
rep.setWebAuthnPolicyAvoidSameAuthenticatorRegister(true);
rep.setWebAuthnPolicyAcceptableAaguids(Collections.singletonList("00000000-0000-0000-0000-000000000000"));
rep.setWebAuthnPolicyPasswordlessRpEntityName("RP_ENTITY_NAME");
rep.setWebAuthnPolicyPasswordlessSignatureAlgorithms(Collections.singletonList("RS256"));
rep.setWebAuthnPolicyPasswordlessRpId("localhost");
rep.setWebAuthnPolicyPasswordlessAttestationConveyancePreference("Direct");
rep.setWebAuthnPolicyPasswordlessAuthenticatorAttachment("Platform");
rep.setWebAuthnPolicyPasswordlessRequireResidentKey("Yes");
rep.setWebAuthnPolicyPasswordlessUserVerificationRequirement("Required");
rep.setWebAuthnPolicyPasswordlessCreateTimeout(dummyInt);
rep.setWebAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister(true);
rep.setWebAuthnPolicyPasswordlessAcceptableAaguids(Collections.singletonList("00000000-0000-0000-0000-000000000000"));
adminClient.realms().create(rep);
RealmRepresentation rep2 = new RealmRepresentation();
rep2.setAttributes(Collections.singletonMap("frontendUrl", "http://localhost/frontEnd"));
adminClient.realm("attributes").update(rep2);
rep = adminClient.realm("attributes").toRepresentation();
assertEquals("DISPLAY_NAME", rep.getDisplayName());
assertEquals("DISPLAY_NAME_HTML", rep.getDisplayNameHtml());
assertEquals("RS256", rep.getDefaultSignatureAlgorithm());
assertTrue(rep.isBruteForceProtected());
assertTrue(rep.isPermanentLockout());
assertEquals(dummyInt, rep.getMaxFailureWaitSeconds());
assertEquals(dummyInt, rep.getWaitIncrementSeconds());
assertEquals(dummyLong, rep.getQuickLoginCheckMilliSeconds());
assertEquals(dummyInt, rep.getMinimumQuickLoginWaitSeconds());
assertEquals(dummyInt, rep.getMaxDeltaTimeSeconds());
assertEquals(dummyInt, rep.getFailureFactor());
assertEquals(dummyInt, rep.getActionTokenGeneratedByAdminLifespan());
assertEquals(dummyInt, rep.getActionTokenGeneratedByUserLifespan());
assertTrue(rep.getOfflineSessionMaxLifespanEnabled());
assertEquals(dummyInt, rep.getOfflineSessionMaxLifespan());
assertEquals("RP_ENTITY_NAME", rep.getWebAuthnPolicyRpEntityName());
assertEquals(Collections.singletonList("RS256"), rep.getWebAuthnPolicySignatureAlgorithms());
assertEquals("localhost", rep.getWebAuthnPolicyRpId());
assertEquals("Direct", rep.getWebAuthnPolicyAttestationConveyancePreference());
assertEquals("Platform", rep.getWebAuthnPolicyAuthenticatorAttachment());
assertEquals("Yes", rep.getWebAuthnPolicyRequireResidentKey());
assertEquals("Required", rep.getWebAuthnPolicyUserVerificationRequirement());
assertEquals(dummyInt, rep.getWebAuthnPolicyCreateTimeout());
assertTrue(rep.isWebAuthnPolicyAvoidSameAuthenticatorRegister());
assertEquals(Collections.singletonList("00000000-0000-0000-0000-000000000000"), rep.getWebAuthnPolicyAcceptableAaguids());
assertEquals("RP_ENTITY_NAME", rep.getWebAuthnPolicyPasswordlessRpEntityName());
assertEquals(Collections.singletonList("RS256"), rep.getWebAuthnPolicyPasswordlessSignatureAlgorithms());
assertEquals("localhost", rep.getWebAuthnPolicyPasswordlessRpId());
assertEquals("Direct", rep.getWebAuthnPolicyPasswordlessAttestationConveyancePreference());
assertEquals("Platform", rep.getWebAuthnPolicyPasswordlessAuthenticatorAttachment());
assertEquals("Yes", rep.getWebAuthnPolicyPasswordlessRequireResidentKey());
assertEquals("Required", rep.getWebAuthnPolicyPasswordlessUserVerificationRequirement());
assertEquals(dummyInt, rep.getWebAuthnPolicyPasswordlessCreateTimeout());
assertTrue(rep.isWebAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister());
assertEquals(Collections.singletonList("00000000-0000-0000-0000-000000000000"), rep.getWebAuthnPolicyPasswordlessAcceptableAaguids());
assertEquals(browserSecurityHeaders, rep.getBrowserSecurityHeaders());
adminClient.realms().realm("attributes").remove();
}
@Test
public void updateRealmAttributes() {
// first change
RealmRepresentation rep = new RealmRepresentation();
List<String> webAuthnPolicyAcceptableAaguids = new ArrayList<>();
webAuthnPolicyAcceptableAaguids.add("aaguid1");
webAuthnPolicyAcceptableAaguids.add("aaguid2");
rep.setAttributes(new HashMap<>());
rep.getAttributes().put("foo1", "bar1");
rep.getAttributes().put("foo2", "bar2");
rep.setWebAuthnPolicyRpEntityName("keycloak");
rep.setWebAuthnPolicyAcceptableAaguids(webAuthnPolicyAcceptableAaguids);
rep.setBruteForceProtected(true);
rep.setDisplayName("dn1");
managedRealm.admin().update(rep);
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).resourceType(ResourceType.REALM);
rep = managedRealm.admin().toRepresentation();
assertEquals("bar1", rep.getAttributes().get("foo1"));
assertEquals("bar2", rep.getAttributes().get("foo2"));
assertTrue(rep.isBruteForceProtected());
assertEquals("dn1", rep.getDisplayName());
assertEquals(webAuthnPolicyAcceptableAaguids, rep.getWebAuthnPolicyAcceptableAaguids());
// second change
webAuthnPolicyAcceptableAaguids.clear();
rep.setBruteForceProtected(false);
rep.setDisplayName("dn2");
rep.getAttributes().put("foo1", "bar11");
rep.getAttributes().remove("foo2");
rep.setWebAuthnPolicyAcceptableAaguids(webAuthnPolicyAcceptableAaguids);
managedRealm.admin().update(rep);
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).resourceType(ResourceType.REALM);
rep = managedRealm.admin().toRepresentation();
assertFalse(rep.isBruteForceProtected());
assertEquals("dn2", rep.getDisplayName());
assertEquals("bar11", rep.getAttributes().get("foo1"));
assertFalse(rep.getAttributes().containsKey("foo2"));
assertTrue(rep.getWebAuthnPolicyAcceptableAaguids().isEmpty());
}
@Test
public void testSetEmptyAttributeValues() {
String realmName = "testSetEmptyAttributeValues";
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realmName);
rep.setAttributes(new HashMap<>());
rep.getAttributes().put("myboolean", "");
rep.getAttributes().put("mylong", "");
rep.getAttributes().put("myint", "");
rep.getAttributes().put(RealmAttributes.ACTION_TOKEN_GENERATED_BY_USER_LIFESPAN + ".something", "");
adminClient.realms().create(rep);
try {
adminClient.realm(realmName);
runOnServer.run(session -> {
RealmModel realm = session.realms().getRealmByName(realmName);
Assertions.assertInstanceOf(RealmAdapter.class, realm);
Assertions.assertNull(realm.getUserActionTokenLifespans().get("something"));
Assertions.assertEquals(true, realm.getAttribute("myboolean", true));
Assertions.assertEquals(realm.getAttribute("mylong", (long) 123), Long.valueOf(123));
Assertions.assertEquals(realm.getAttribute("myint", 1234), Integer.valueOf(1234));
RealmProvider delegate = session.getProvider(CacheRealmProvider.class).getRealmDelegate();
RealmModel realm2 = delegate.getRealm(realm.getId());
Assertions.assertInstanceOf(org.keycloak.models.jpa.RealmAdapter.class, realm2);
Assertions.assertNull(realm2.getUserActionTokenLifespans().get("something"));
Assertions.assertEquals(true, realm2.getAttribute("myboolean", true));
Assertions.assertEquals(realm2.getAttribute("mylong", (long) 123), Long.valueOf(123));
Assertions.assertEquals(realm2.getAttribute("myint", 1234), Integer.valueOf(1234));
});
} finally {
adminClient.realm(realmName).remove();
}
}
}

View File

@ -0,0 +1,81 @@
package org.keycloak.tests.admin.realm;
import jakarta.ws.rs.core.Response;
import org.infinispan.Cache;
import org.junit.jupiter.api.Test;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.events.AdminEventAssertion;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
import org.keycloak.tests.utils.admin.ApiUtil;
import org.keycloak.tests.utils.admin.AdminEventPaths;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@KeycloakIntegrationTest
public class RealmCacheTest extends AbstractRealmTest {
@InjectRealm(ref = "master", attachTo = "master")
ManagedRealm masterRealm;
@InjectRunOnServer(ref = "master", realmRef = "master")
RunOnServerClient masterRunOnServer;
@Test
public void clearRealmCache() {
String realmId = managedRealm.getId();
assertTrue(runOnServer.fetch(s -> {
InfinispanConnectionProvider provider = s.getProvider(InfinispanConnectionProvider.class);
Cache<Object, Object> cache = provider.getCache("realms");
return cache.containsKey(realmId);
}, Boolean.class));
managedRealm.admin().clearRealmCache();
// Using master realm to verify that managedRealm cache is empty.
assertFalse(masterRunOnServer.fetch(s -> {
InfinispanConnectionProvider provider = s.getProvider(InfinispanConnectionProvider.class);
Cache<Object, Object> cache = provider.getCache("realms");
return cache.containsKey(realmId);
}, Boolean.class));
// The Admin event must be checked after the verification, because the event poll recreates the cache!
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, "clear-realm-cache", ResourceType.REALM);
}
@Test
public void clearUserCache() {
UserRepresentation user = new UserRepresentation();
user.setUsername("clearcacheuser");
Response response = managedRealm.admin().users().create(user);
String userId = ApiUtil.getCreatedId(response);
response.close();
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.userResourcePath(userId), user, ResourceType.USER);
managedRealm.admin().users().get(userId).toRepresentation();
assertTrue(runOnServer.fetch(s -> {
InfinispanConnectionProvider provider = s.getProvider(InfinispanConnectionProvider.class);
Cache<Object, Object> cache = provider.getCache("users");
return cache.containsKey(userId);
}, Boolean.class));
managedRealm.admin().clearUserCache();
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, "clear-user-cache", ResourceType.REALM);
assertFalse(runOnServer.fetch(s -> {
InfinispanConnectionProvider provider = s.getProvider(InfinispanConnectionProvider.class);
Cache<Object, Object> cache = provider.getCache("users");
return cache.containsKey(userId);
}, Boolean.class));
}
// NOTE: clearKeysCache tested in KcOIDCBrokerWithSignatureTest
}

View File

@ -0,0 +1,262 @@
package org.keycloak.tests.admin.realm;
import com.google.common.collect.ImmutableSet;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.NotFoundException;
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.models.AdminRoles;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.realm.UserConfigBuilder;
import org.keycloak.tests.utils.Assert;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.everyItem;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@KeycloakIntegrationTest
public class RealmCreateTest extends AbstractRealmTest {
@Test
public void getRealms() {
List<RealmRepresentation> realms = adminClient.realms().findAll();
Assert.assertNames(realms, "master", managedRealm.getName());
}
@Test
public void getRealmRepresentation() {
RealmRepresentation rep = managedRealm.admin().toRepresentation();
Assertions.assertEquals(managedRealm.getName(), rep.getRealm());
assertTrue(rep.isEnabled());
}
@Test
public void createRealmEmpty() {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm("new-realm");
adminClient.realms().create(rep);
Assert.assertNames(adminClient.realms().findAll(), "master", managedRealm.getName(), "new-realm");
List<String> clients = adminClient.realms().realm("new-realm").clients().findAll().stream().map(ClientRepresentation::getClientId).collect(Collectors.toList());
assertThat(clients, containsInAnyOrder("account", "account-console", "admin-cli", "broker", "realm-management", "security-admin-console"));
adminClient.realms().realm("new-realm").remove();
Assert.assertNames(adminClient.realms().findAll(), "master", managedRealm.getName());
}
@Test
public void createRealmWithValidConsoleUris() throws Exception {
var realmNameWithSpaces = "new realm";
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realmNameWithSpaces);
rep.setEnabled(Boolean.TRUE);
rep.setUsers(Collections.singletonList(UserConfigBuilder.create()
.username("new-realm-admin")
.name("new-realm-admin", "new-realm-admin")
.email("new-realm-admin@keycloak.org")
.emailVerified()
.password("password")
.clientRoles(Constants.REALM_MANAGEMENT_CLIENT_ID, AdminRoles.REALM_ADMIN)
.build()));
adminClient.realms().create(rep);
Assert.assertNames(adminClient.realms().findAll(), "master", managedRealm.getName(), realmNameWithSpaces);
final var urlPlaceHolders = ImmutableSet.of("${authBaseUrl}", "${authAdminUrl}");
RealmResource newRealm = adminClient.realms().realm(realmNameWithSpaces);
List<String> clientUris = newRealm.clients()
.findAll()
.stream()
.flatMap(client -> Stream.concat(Stream.concat(Stream.concat(
client.getRedirectUris().stream(),
Stream.of(client.getBaseUrl())),
Stream.of(client.getRootUrl())),
Stream.of(client.getAdminUrl())))
.filter(Objects::nonNull)
.filter(uri -> !urlPlaceHolders.contains(uri))
.collect(Collectors.toList());
assertThat(clientUris, not(empty()));
assertThat(clientUris, everyItem(containsString("/new%20realm/")));
try (Keycloak client = adminClientFactory.create().realm(realmNameWithSpaces)
.username("new-realm-admin").password("password").clientId(Constants.ADMIN_CLI_CLIENT_ID).build()) {
Assertions.assertNotNull(client.serverInfo().getInfo());
}
adminClient.realms().realm(realmNameWithSpaces).remove();
Assert.assertNames(adminClient.realms().findAll(), "master", managedRealm.getName());
}
@Test
public void createRealmRejectReservedCharOrEmptyName() {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm("new-re;alm");
assertThrows(BadRequestException.class, () -> adminClient.realms().create(rep));
rep.setRealm("");
assertThrows(BadRequestException.class, () -> adminClient.realms().create(rep));
rep.setRealm("new-realm");
rep.setId("invalid;id");
assertThrows(BadRequestException.class, () -> adminClient.realms().create(rep));
}
@Test
public void createRealmCheckDefaultPasswordPolicy() {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm("new-realm");
adminClient.realms().create(rep);
assertNull(adminClient.realm("new-realm").toRepresentation().getPasswordPolicy());
adminClient.realms().realm("new-realm").remove();
rep.setPasswordPolicy("length(8)");
adminClient.realms().create(rep);
assertEquals("length(8)", adminClient.realm("new-realm").toRepresentation().getPasswordPolicy());
adminClient.realms().realm("new-realm").remove();
}
@Test
public void createRealmFromJson() throws IOException {
RealmRepresentation rep = JsonSerialization.readValue(getClass().getResourceAsStream("testrealm.json"), RealmRepresentation.class);
adminClient.realms().create(rep);
RealmRepresentation created = adminClient.realms().realm("admin-test-1").toRepresentation();
assertRealm(rep, created);
adminClient.realms().realm("admin-test-1").remove();
}
//KEYCLOAK-6146
@Test
public void createRealmWithPasswordPolicyFromJsonWithInvalidPasswords() throws IOException {
RealmRepresentation rep = JsonSerialization.readValue(getClass().getResourceAsStream("testrealm-keycloak-6146-error.json"), RealmRepresentation.class);
//try to create realm with password policies and users with plain-text passwords what doesn't met the policies
try {
adminClient.realms().create(rep);
} catch (BadRequestException ex) {
//ensure the realm was not created
Assertions.assertThrows(NotFoundException.class, () -> {
adminClient.realms().realm("secure-app").toRepresentation();
});
}
}
//KEYCLOAK-6146
@Test
public void createRealmWithPasswordPolicyFromJsonWithValidPasswords() throws IOException {
RealmRepresentation rep = JsonSerialization.readValue(RealmCreateTest.class.getResourceAsStream("testrealm-keycloak-6146.json"), RealmRepresentation.class);
adminClient.realms().create(rep);
assertRealm(rep, adminClient.realm(rep.getRealm()).toRepresentation());
adminClient.realms().realm(rep.getRealm()).remove();
}
private void assertRealm(RealmRepresentation realm, RealmRepresentation storedRealm) {
if (realm.getRealm() != null) {
assertEquals(realm.getRealm(), storedRealm.getRealm());
}
if (realm.isEnabled() != null) assertEquals(realm.isEnabled(), storedRealm.isEnabled());
if (realm.isBruteForceProtected() != null) assertEquals(realm.isBruteForceProtected(), storedRealm.isBruteForceProtected());
if (realm.getMaxFailureWaitSeconds() != null) assertEquals(realm.getMaxFailureWaitSeconds(), storedRealm.getMaxFailureWaitSeconds());
if (realm.getMinimumQuickLoginWaitSeconds() != null) assertEquals(realm.getMinimumQuickLoginWaitSeconds(), storedRealm.getMinimumQuickLoginWaitSeconds());
if (realm.getWaitIncrementSeconds() != null) assertEquals(realm.getWaitIncrementSeconds(), storedRealm.getWaitIncrementSeconds());
if (realm.getQuickLoginCheckMilliSeconds() != null) assertEquals(realm.getQuickLoginCheckMilliSeconds(), storedRealm.getQuickLoginCheckMilliSeconds());
if (realm.getMaxDeltaTimeSeconds() != null) assertEquals(realm.getMaxDeltaTimeSeconds(), storedRealm.getMaxDeltaTimeSeconds());
if (realm.getFailureFactor() != null) assertEquals(realm.getFailureFactor(), storedRealm.getFailureFactor());
if (realm.isRegistrationAllowed() != null) assertEquals(realm.isRegistrationAllowed(), storedRealm.isRegistrationAllowed());
if (realm.isRegistrationEmailAsUsername() != null) assertEquals(realm.isRegistrationEmailAsUsername(), storedRealm.isRegistrationEmailAsUsername());
if (realm.isRememberMe() != null) assertEquals(realm.isRememberMe(), storedRealm.isRememberMe());
if (realm.isVerifyEmail() != null) assertEquals(realm.isVerifyEmail(), storedRealm.isVerifyEmail());
if (realm.isLoginWithEmailAllowed() != null) assertEquals(realm.isLoginWithEmailAllowed(), storedRealm.isLoginWithEmailAllowed());
if (realm.isDuplicateEmailsAllowed() != null) assertEquals(realm.isDuplicateEmailsAllowed(), storedRealm.isDuplicateEmailsAllowed());
if (realm.isResetPasswordAllowed() != null) assertEquals(realm.isResetPasswordAllowed(), storedRealm.isResetPasswordAllowed());
if (realm.isEditUsernameAllowed() != null) assertEquals(realm.isEditUsernameAllowed(), storedRealm.isEditUsernameAllowed());
if (realm.getSslRequired() != null) assertEquals(realm.getSslRequired(), storedRealm.getSslRequired());
if (realm.getAccessCodeLifespan() != null) assertEquals(realm.getAccessCodeLifespan(), storedRealm.getAccessCodeLifespan());
if (realm.getAccessCodeLifespanUserAction() != null)
assertEquals(realm.getAccessCodeLifespanUserAction(), storedRealm.getAccessCodeLifespanUserAction());
if (realm.getActionTokenGeneratedByAdminLifespan() != null)
assertEquals(realm.getActionTokenGeneratedByAdminLifespan(), storedRealm.getActionTokenGeneratedByAdminLifespan());
if (realm.getActionTokenGeneratedByUserLifespan() != null)
assertEquals(realm.getActionTokenGeneratedByUserLifespan(), storedRealm.getActionTokenGeneratedByUserLifespan());
else
assertEquals(realm.getAccessCodeLifespanUserAction(), storedRealm.getActionTokenGeneratedByUserLifespan());
if (realm.getNotBefore() != null) assertEquals(realm.getNotBefore(), storedRealm.getNotBefore());
if (realm.getAccessTokenLifespan() != null) assertEquals(realm.getAccessTokenLifespan(), storedRealm.getAccessTokenLifespan());
if (realm.getAccessTokenLifespanForImplicitFlow() != null) assertEquals(realm.getAccessTokenLifespanForImplicitFlow(), storedRealm.getAccessTokenLifespanForImplicitFlow());
if (realm.getSsoSessionIdleTimeout() != null) assertEquals(realm.getSsoSessionIdleTimeout(), storedRealm.getSsoSessionIdleTimeout());
if (realm.getSsoSessionMaxLifespan() != null) assertEquals(realm.getSsoSessionMaxLifespan(), storedRealm.getSsoSessionMaxLifespan());
if (realm.getSsoSessionIdleTimeoutRememberMe() != null) Assert.assertEquals(realm.getSsoSessionIdleTimeoutRememberMe(), storedRealm.getSsoSessionIdleTimeoutRememberMe());
if (realm.getSsoSessionMaxLifespanRememberMe() != null) Assert.assertEquals(realm.getSsoSessionMaxLifespanRememberMe(), storedRealm.getSsoSessionMaxLifespanRememberMe());
if (realm.getClientSessionIdleTimeout() != null)
Assertions.assertEquals(realm.getClientSessionIdleTimeout(), storedRealm.getClientSessionIdleTimeout());
if (realm.getClientSessionMaxLifespan() != null)
Assertions.assertEquals(realm.getClientSessionMaxLifespan(), storedRealm.getClientSessionMaxLifespan());
if (realm.getClientOfflineSessionIdleTimeout() != null)
Assertions.assertEquals(realm.getClientOfflineSessionIdleTimeout(), storedRealm.getClientOfflineSessionIdleTimeout());
if (realm.getClientOfflineSessionMaxLifespan() != null)
Assertions.assertEquals(realm.getClientOfflineSessionMaxLifespan(), storedRealm.getClientOfflineSessionMaxLifespan());
if (realm.getRequiredCredentials() != null) {
assertNotNull(storedRealm.getRequiredCredentials());
for (String cred : realm.getRequiredCredentials()) {
assertTrue(storedRealm.getRequiredCredentials().contains(cred));
}
}
if (realm.getLoginTheme() != null) assertEquals(realm.getLoginTheme(), storedRealm.getLoginTheme());
if (realm.getAccountTheme() != null) assertEquals(realm.getAccountTheme(), storedRealm.getAccountTheme());
if (realm.getAdminTheme() != null) assertEquals(realm.getAdminTheme(), storedRealm.getAdminTheme());
if (realm.getEmailTheme() != null) assertEquals(realm.getEmailTheme(), storedRealm.getEmailTheme());
if (realm.getPasswordPolicy() != null) assertEquals(realm.getPasswordPolicy(), storedRealm.getPasswordPolicy());
if (realm.getSmtpServer() != null) {
assertEquals(realm.getSmtpServer(), storedRealm.getSmtpServer());
}
if (realm.getBrowserSecurityHeaders() != null) {
assertEquals(realm.getBrowserSecurityHeaders(), storedRealm.getBrowserSecurityHeaders());
}
if (realm.getAttributes() != null) {
HashMap<String, String> attributes = new HashMap<>();
attributes.putAll(storedRealm.getAttributes());
attributes.entrySet().retainAll(realm.getAttributes().entrySet());
assertEquals(realm.getAttributes(), attributes);
}
if (realm.isUserManagedAccessAllowed() != null) assertEquals(realm.isUserManagedAccessAllowed(), storedRealm.isUserManagedAccessAllowed());
}
}

View File

@ -0,0 +1,224 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.tests.admin.realm;
import jakarta.ws.rs.NotFoundException;
import org.apache.commons.io.IOUtils;
import org.bouncycastle.util.Strings;
import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.AdminEventRepresentation;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.ComponentRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.testframework.annotations.InjectAdminEvents;
import org.keycloak.testframework.annotations.InjectRealm;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.events.AdminEventAssertion;
import org.keycloak.testframework.events.AdminEvents;
import org.keycloak.testframework.realm.ManagedRealm;
import org.keycloak.testframework.remote.runonserver.InjectRunOnServer;
import org.keycloak.testframework.remote.runonserver.RunOnServerClient;
import org.keycloak.tests.utils.admin.AdminEventPaths;
import org.keycloak.tests.utils.runonserver.RunHelpers;
import org.keycloak.util.JsonSerialization;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@KeycloakIntegrationTest
public class RealmDefaultConfigTest extends AbstractRealmTest {
@InjectRealm(ref = "realm-with-smtp")
ManagedRealm smtpRealm;
@InjectRunOnServer(ref = "smtp", realmRef = "realm-with-smtp")
RunOnServerClient smtpRealmRunOnServer;
@InjectAdminEvents(ref = "smtpEvents", realmRef = "realm-with-smtp")
AdminEvents smtpRealmAdminEvents;
@Test
public void smtpPasswordSecret() {
smtpRealm.updateWithCleanup(r -> r.smtp("localhost",3025, "smtp_realm@local"));
RealmRepresentation rep = smtpRealm.admin().toRepresentation();
rep.getSmtpServer().put("auth", "true");
rep.getSmtpServer().put("user", "user");
rep.getSmtpServer().put("password", "secret");
smtpRealm.admin().update(rep);
smtpRealmAdminEvents.clear();
RealmRepresentation returned = smtpRealm.admin().toRepresentation();
assertEquals(ComponentRepresentation.SECRET_VALUE, returned.getSmtpServer().get("password"));
RealmRepresentation internalRep = smtpRealmRunOnServer.fetch(RunHelpers.internalRealm());
assertEquals("secret", internalRep.getSmtpServer().get("password"));
smtpRealm.admin().update(rep);
AdminEventRepresentation event = smtpRealmAdminEvents.poll();
assertFalse(event.getRepresentation().contains("some secret value!!"));
assertTrue(event.getRepresentation().contains(ComponentRepresentation.SECRET_VALUE));
internalRep = smtpRealmRunOnServer.fetch(RunHelpers.internalRealm());
assertEquals("secret", internalRep.getSmtpServer().get("password"));
RealmRepresentation realm = adminClient.realms().findAll().stream()
.filter(r -> r.getRealm().equals("realm-with-smtp")).findFirst().get();
assertEquals(ComponentRepresentation.SECRET_VALUE, realm.getSmtpServer().get("password"));
// updating setting the secret value with asterisks
rep.getSmtpServer().put("password", ComponentRepresentation.SECRET_VALUE);
smtpRealm.admin().update(rep);
event = smtpRealmAdminEvents.poll();
assertTrue(event.getRepresentation().contains(ComponentRepresentation.SECRET_VALUE));
internalRep = smtpRealmRunOnServer.fetch(RunHelpers.internalRealm());
assertEquals("secret", internalRep.getSmtpServer().get("password"));
realm = smtpRealm.admin().toRepresentation();
assertEquals(ComponentRepresentation.SECRET_VALUE, realm.getSmtpServer().get("password"));
}
@Test
// KEYCLOAK-1110
public void deleteDefaultRole() {
RoleRepresentation role = new RoleRepresentation("test", "test", false);
managedRealm.admin().roles().create(role);
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.roleResourcePath("test"), role, ResourceType.REALM_ROLE);
role = managedRealm.admin().roles().get("test").toRepresentation();
assertNotNull(role);
managedRealm.admin().roles().get(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + Strings.toLowerCase(managedRealm.getName())).addComposites(Collections.singletonList(role));
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.roleResourceCompositesPath(Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + Strings.toLowerCase(managedRealm.getName())), Collections.singletonList(role), ResourceType.REALM_ROLE);
managedRealm.admin().roles().deleteRole("test");
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.roleResourcePath("test"), ResourceType.REALM_ROLE);
try {
managedRealm.admin().roles().get("testsadfsadf").toRepresentation();
fail("Expected NotFoundException");
} catch (NotFoundException e) {
// Expected
}
}
@Test
public void convertKeycloakClientDescription() throws IOException {
ClientRepresentation description = new ClientRepresentation();
description.setClientId("client-id");
description.setRedirectUris(Collections.singletonList("http://localhost"));
ClientRepresentation converted = managedRealm.admin().convertClientDescription(JsonSerialization.writeValueAsString(description));
assertEquals("client-id", converted.getClientId());
assertEquals("http://localhost", converted.getRedirectUris().get(0));
}
@Test
public void convertOIDCClientDescription() throws IOException {
String description = IOUtils.toString(RealmDefaultConfigTest.class.getResourceAsStream("client-oidc.json"), Charset.defaultCharset());
ClientRepresentation converted = managedRealm.admin().convertClientDescription(description);
assertEquals(1, converted.getRedirectUris().size());
assertEquals("http://localhost", converted.getRedirectUris().get(0));
}
@Test
public void convertSAMLClientDescription() throws IOException {
String description = IOUtils.toString(RealmDefaultConfigTest.class.getResourceAsStream("saml-entity-descriptor.xml"), Charset.defaultCharset());
ClientRepresentation converted = managedRealm.admin().convertClientDescription(description);
assertEquals("loadbalancer-9.siroe.com", converted.getClientId());
assertEquals(2, converted.getRedirectUris().size());
assertEquals("https://LoadBalancer-9.siroe.com:3443/federation/Consumer/metaAlias/sp", converted.getRedirectUris().get(0));
assertEquals("https://LoadBalancer-9.siroe.com:3443/federation/Consumer/metaAlias/sp", converted.getRedirectUris().get(1));
}
@Test
// KEYCLOAK-17342
public void testDefaultSignatureAlgorithm() {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm("new-realm");
adminClient.realms().create(rep);
assertEquals(Constants.DEFAULT_SIGNATURE_ALGORITHM, adminClient.realm("master").toRepresentation().getDefaultSignatureAlgorithm());
assertEquals(Constants.DEFAULT_SIGNATURE_ALGORITHM, adminClient.realm("new-realm").toRepresentation().getDefaultSignatureAlgorithm());
adminClient.realms().realm("new-realm").remove();
}
@Test
public void testSupportedOTPApplications() {
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm("new-realm");
adminClient.realms().create(rep);
RealmResource realm = adminClient.realms().realm("new-realm");
rep = realm.toRepresentation();
List<String> supportedApplications = rep.getOtpSupportedApplications();
assertThat(supportedApplications, hasSize(3));
assertThat(supportedApplications, containsInAnyOrder("totpAppGoogleName", "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName"));
rep.setOtpPolicyDigits(8);
realm.update(rep);
rep = realm.toRepresentation();
supportedApplications = rep.getOtpSupportedApplications();
assertThat(supportedApplications, hasSize(2));
assertThat(supportedApplications, containsInAnyOrder("totpAppFreeOTPName", "totpAppGoogleName"));
rep.setOtpPolicyType("hotp");
realm.update(rep);
rep = realm.toRepresentation();
supportedApplications = rep.getOtpSupportedApplications();
assertThat(supportedApplications, hasSize(2));
assertThat(supportedApplications, containsInAnyOrder("totpAppFreeOTPName", "totpAppGoogleName"));
adminClient.realms().realm("new-realm").remove();
}
}

View File

@ -0,0 +1,250 @@
package org.keycloak.tests.admin.realm;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.core.Response;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.common.util.Time;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.saml.SamlProtocol;
import org.keycloak.representations.adapters.action.GlobalRequestResult;
import org.keycloak.representations.adapters.action.PushNotBeforeAction;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testframework.annotations.InjectEvents;
import org.keycloak.testframework.annotations.InjectKeycloakUrls;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.events.AdminEventAssertion;
import org.keycloak.testframework.events.Events;
import org.keycloak.testframework.oauth.OAuthClient;
import org.keycloak.testframework.oauth.TestApp;
import org.keycloak.testframework.oauth.annotations.InjectOAuthClient;
import org.keycloak.testframework.oauth.annotations.InjectTestApp;
import org.keycloak.testframework.realm.ClientConfigBuilder;
import org.keycloak.testframework.realm.UserConfigBuilder;
import org.keycloak.testframework.server.KeycloakUrls;
import org.keycloak.tests.utils.admin.ApiUtil;
import org.keycloak.tests.utils.admin.AdminEventPaths;
import org.keycloak.testsuite.util.oauth.AccessTokenResponse;
import java.util.List;
import java.util.Map;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@KeycloakIntegrationTest
public class RealmOAuthActionsTest extends AbstractRealmTest {
@InjectOAuthClient(ref = "managedOAuth", realmRef = "managedRealm")
OAuthClient oauth;
@InjectTestApp
TestApp testApp;
@InjectEvents(ref = "managedEvents", realmRef = "managedRealm")
Events events;
@InjectKeycloakUrls
KeycloakUrls keycloakUrls;
@Test
public void pushNotBefore() throws InterruptedException {
setupTestAppAndUser();
int time = Time.currentTime() - 60;
RealmRepresentation rep = managedRealm.admin().toRepresentation();
rep.setNotBefore(time);
managedRealm.admin().update(rep);
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).representation(rep).resourceType(ResourceType.REALM);
GlobalRequestResult globalRequestResult = managedRealm.admin().pushRevocation();
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, "push-revocation", globalRequestResult, ResourceType.REALM);
assertThat(globalRequestResult.getSuccessRequests(), containsInAnyOrder(testApp.getAdminUri()));
assertNull(globalRequestResult.getFailedRequests());
PushNotBeforeAction adminPushNotBefore = testApp.kcAdmin().getAdminPushNotBefore();
assertEquals(time, adminPushNotBefore.getNotBefore());
managedRealm.admin().clients().get("test-app-new").remove();
managedRealm.admin().users().get(ApiUtil.findUserByUsername(managedRealm.admin(), "testuser").getId()).remove();
}
@Test
public void pushNotBeforeWithSamlApp() throws InterruptedException {
setupTestAppAndUser();
setupTestSamlApp();
int time = Time.currentTime() - 60;
RealmRepresentation rep = managedRealm.admin().toRepresentation();
rep.setNotBefore(time);
managedRealm.admin().update(rep);
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).representation(rep).resourceType(ResourceType.REALM);
GlobalRequestResult globalRequestResult = managedRealm.admin().pushRevocation();
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, "push-revocation", globalRequestResult, ResourceType.REALM);
assertThat(globalRequestResult.getSuccessRequests(), containsInAnyOrder(testApp.getAdminUri()));
assertThat(globalRequestResult.getFailedRequests(), containsInAnyOrder(keycloakUrls.getBase() + "/realms/" + managedRealm.getName() + "/saml-app/saml"));
PushNotBeforeAction adminPushNotBefore = testApp.kcAdmin().getAdminPushNotBefore();
assertEquals(time, adminPushNotBefore.getNotBefore());
managedRealm.admin().clients().get("test-saml-app").remove();
managedRealm.admin().clients().get("test-app-new").remove();
managedRealm.admin().users().get(ApiUtil.findUserByUsername(managedRealm.admin(), "testuser").getId()).remove();
}
@Test
public void logoutAll() throws InterruptedException {
setupTestAppAndUser();
Response response = managedRealm.admin().users().create(UserConfigBuilder.create().username("user").name("User", "Name").email("user@name").emailVerified().build());
String userId = ApiUtil.getCreatedId(response);
response.close();
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.userResourcePath(userId), ResourceType.USER);
CredentialRepresentation credential = new CredentialRepresentation();
credential.setType(CredentialRepresentation.PASSWORD);
credential.setValue("password");
managedRealm.admin().users().get(userId).resetPassword(credential);
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResetPasswordPath(userId), ResourceType.USER);
oauth.doPasswordGrantRequest("user", "password");
GlobalRequestResult globalRequestResult = managedRealm.admin().logoutAll();
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, "logout-all", globalRequestResult, ResourceType.REALM);
assertEquals(1, globalRequestResult.getSuccessRequests().size());
assertEquals(testApp.getAdminUri(), globalRequestResult.getSuccessRequests().get(0));
assertNull(globalRequestResult.getFailedRequests());
assertNotNull(testApp.kcAdmin().getAdminLogoutAction());
managedRealm.admin().clients().get("test-app-new").remove();
managedRealm.admin().users().get(ApiUtil.findUserByUsername(managedRealm.admin(), "testuser").getId()).remove();
}
@Test
public void deleteSession() {
setupTestAppAndUser();
oauth.doLogin("testuser", "password");
AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(oauth.parseLoginResponse().getCode());
assertEquals(200, tokenResponse.getStatusCode());
EventRepresentation event = events.poll();
assertNotNull(event);
managedRealm.admin().deleteSession(event.getSessionId(), false);
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.DELETE, AdminEventPaths.deleteSessionPath(event.getSessionId()), ResourceType.USER_SESSION);
try {
managedRealm.admin().deleteSession(event.getSessionId(), false);
fail("Expected 404");
} catch (NotFoundException e) {
// Expected
Assertions.assertNull(adminEvents.poll());
}
tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken());
assertEquals(400, tokenResponse.getStatusCode());
assertEquals("Session not active", tokenResponse.getErrorDescription());
managedRealm.admin().clients().get("test-app-new").remove();
managedRealm.admin().users().get(ApiUtil.findUserByUsername(managedRealm.admin(), "testuser").getId()).remove();
}
@Test
public void clientSessionStats() {
setupTestAppAndUser();
List<Map<String, String>> sessionStats = managedRealm.admin().getClientSessionStats();
assertTrue(sessionStats.isEmpty());
oauth.doLogin("testuser", "password");
AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(oauth.parseLoginResponse().getCode());
assertEquals(200, tokenResponse.getStatusCode());
sessionStats = managedRealm.admin().getClientSessionStats();
assertEquals(1, sessionStats.size());
assertEquals("test-app-new", sessionStats.get(0).get("clientId"));
assertEquals("1", sessionStats.get(0).get("active"));
String clientUuid = sessionStats.get(0).get("id");
managedRealm.admin().clients().get(clientUuid).remove();
sessionStats = managedRealm.admin().getClientSessionStats();
assertEquals(0, sessionStats.size());
managedRealm.admin().users().get(ApiUtil.findUserByUsername(managedRealm.admin(), "testuser").getId()).remove();
}
private void setupTestAppAndUser() {
testApp.kcAdmin().clear();
ClientRepresentation client = ClientConfigBuilder.create()
.id("test-app-new")
.clientId("test-app-new")
.protocol(OIDCLoginProtocol.LOGIN_PROTOCOL)
.adminUrl(testApp.getAdminUri())
.redirectUris(testApp.getRedirectionUri())
.secret("secret")
.build();
Response resp = managedRealm.admin().clients().create(client);
String clientDbId = ApiUtil.getCreatedId(resp);
resp.close();
client.setSecret("**********"); // secrets are masked in events
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.clientResourcePath(clientDbId), client, ResourceType.CLIENT);
oauth.client("test-app-new", "secret");
UserRepresentation userRep = UserConfigBuilder.create().username("testuser").name("Test", "User").email("test@user").emailVerified().build();
Response response = managedRealm.admin().users().create(userRep);
String userId = ApiUtil.getCreatedId(response);
response.close();
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.userResourcePath(userId), userRep, ResourceType.USER);
CredentialRepresentation credential = new CredentialRepresentation();
credential.setType(CredentialRepresentation.PASSWORD);
credential.setValue("password");
managedRealm.admin().users().get(userId).resetPassword(credential);
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.ACTION, AdminEventPaths.userResetPasswordPath(userId), ResourceType.USER);
testApp.kcAdmin().clear();
}
private void setupTestSamlApp() {
ClientRepresentation client = ClientConfigBuilder.create()
.id("test-saml-app")
.clientId("test-saml-app")
.protocol(SamlProtocol.LOGIN_PROTOCOL)
.adminUrl(keycloakUrls.getBase() + "/realms/" + managedRealm.getName() + "/saml-app/saml")
.redirectUris(oauth.getRedirectUri())
.secret("secret")
.build();
Response resp = managedRealm.admin().clients().create(client);
String clientDbId = ApiUtil.getCreatedId(resp);
resp.close();
client.setSecret("**********"); // secrets are masked in events
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.CREATE, AdminEventPaths.clientResourcePath(clientDbId), client, ResourceType.CLIENT);
}
}

View File

@ -0,0 +1,48 @@
package org.keycloak.tests.admin.realm;
import jakarta.ws.rs.BadRequestException;
import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.Keycloak;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.tests.utils.Assert;
import static org.junit.jupiter.api.Assertions.fail;
@KeycloakIntegrationTest
public class RealmRemoveTest extends AbstractRealmTest {
@Test
public void removeRealm() {
RealmRepresentation realmRep = managedRealm.admin().toRepresentation();
adminClient.realm(managedRealm.getName()).remove();
Assert.assertNames(adminClient.realms().findAll(), "master");
adminClient.realms().create(realmRep);
}
@Test
public void removeMasterRealm() {
// any attempt to remove the master realm should fail.
try {
adminClient.realm("master").remove();
fail("It should not be possible to remove the master realm");
} catch(BadRequestException ignored) {
}
}
@Test
public void loginAfterRemoveRealm() {
RealmRepresentation realmRep = managedRealm.admin().toRepresentation();
adminClient.realm(managedRealm.getName()).remove();
try (Keycloak client = adminClientFactory.create().realm("master")
.username("admin").password("admin").clientId(Constants.ADMIN_CLI_CLIENT_ID).build()) {
client.serverInfo().getInfo();
}
adminClient.realms().create(realmRep);
}
}

View File

@ -0,0 +1,289 @@
package org.keycloak.tests.admin.realm;
import jakarta.ws.rs.BadRequestException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.keycloak.events.EventType;
import org.keycloak.events.admin.OperationType;
import org.keycloak.events.admin.ResourceType;
import org.keycloak.events.log.JBossLoggingEventListenerProviderFactory;
import org.keycloak.models.Constants;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testframework.annotations.KeycloakIntegrationTest;
import org.keycloak.testframework.events.AdminEventAssertion;
import org.keycloak.userprofile.UserProfileProvider;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.notNullValue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@KeycloakIntegrationTest
public class RealmUpdateTest extends AbstractRealmTest {
/**
* KEYCLOAK-1990 1991
*/
@Test
public void renameRealmTest() {
RealmRepresentation realm1 = new RealmRepresentation();
realm1.setRealm("test-immutable");
adminClient.realms().create(realm1);
realm1 = adminClient.realms().realm("test-immutable").toRepresentation();
realm1.setRealm("test-immutable-old");
adminClient.realms().realm("test-immutable").update(realm1);
assertThat(adminClient.realms().realm("test-immutable-old").toRepresentation(), notNullValue());
RealmRepresentation realm2 = new RealmRepresentation();
realm2.setRealm("test-immutable");
adminClient.realms().create(realm2);
assertThat(adminClient.realms().realm("test-immutable").toRepresentation(), notNullValue());
adminClient.realms().realm("test-immutable").remove();
adminClient.realms().realm("test-immutable-old").remove();
}
@Test
public void renameRealm() {
String OLD = "old";
String NEW = "new";
RealmRepresentation rep = new RealmRepresentation();
rep.setId(OLD);
rep.setRealm(OLD);
adminClient.realms().create(rep);
Map<String, String> newBaseUrls = new HashMap<>();
Map<String, List<String>> newRedirectUris = new HashMap<>();
// memorize all existing clients with their soon-to-be URIs
adminClient.realm(OLD).clients().findAll().forEach(client -> {
if (client.getBaseUrl() != null && client.getBaseUrl().contains("/" + OLD + "/")) {
newBaseUrls.put(client.getClientId(), client.getBaseUrl().replace("/" + OLD + "/", "/" + NEW + "/"));
}
if (client.getRedirectUris() != null) {
newRedirectUris.put(
client.getClientId(),
client.getRedirectUris()
.stream()
.map(redirectUri -> redirectUri.replace("/" + OLD + "/", "/" + NEW + "/"))
.collect(Collectors.toList())
);
}
});
// at least those three default clients should be in the list of things to be tested
assertThat(newBaseUrls.keySet(), hasItems(Constants.ADMIN_CONSOLE_CLIENT_ID, Constants.ACCOUNT_MANAGEMENT_CLIENT_ID, Constants.ACCOUNT_CONSOLE_CLIENT_ID));
assertThat(newRedirectUris.keySet(), hasItems(Constants.ADMIN_CONSOLE_CLIENT_ID, Constants.ACCOUNT_MANAGEMENT_CLIENT_ID, Constants.ACCOUNT_CONSOLE_CLIENT_ID));
rep.setRealm(NEW);
adminClient.realm(OLD).update(rep);
// Check client in master realm renamed
Assertions.assertEquals(0, adminClient.realm("master").clients().findByClientId("old-realm").size());
Assertions.assertEquals(1, adminClient.realm("master").clients().findByClientId("new-realm").size());
ClientRepresentation adminConsoleClient = adminClient.realm(NEW).clients().findByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID).get(0);
assertEquals(Constants.AUTH_ADMIN_URL_PROP, adminConsoleClient.getRootUrl());
ClientRepresentation accountClient = adminClient.realm(NEW).clients().findByClientId(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID).get(0);
assertEquals(Constants.AUTH_BASE_URL_PROP, accountClient.getRootUrl());
ClientRepresentation accountConsoleClient = adminClient.realm(NEW).clients().findByClientId(Constants.ACCOUNT_CONSOLE_CLIENT_ID).get(0);
assertEquals(Constants.AUTH_BASE_URL_PROP, accountConsoleClient.getRootUrl());
newBaseUrls.forEach((clientId, baseUrl) -> {
assertEquals(baseUrl, adminClient.realm(NEW).clients().findByClientId(clientId).get(0).getBaseUrl());
});
newRedirectUris.forEach((clientId, redirectUris) -> {
assertEquals(redirectUris, adminClient.realm(NEW).clients().findByClientId(clientId).get(0).getRedirectUris());
});
adminClient.realms().realm(NEW).remove();
}
@Test
public void updateRealmEventsConfig() {
RealmEventsConfigRepresentation rep = managedRealm.admin().getRealmEventsConfig();
RealmEventsConfigRepresentation repOrig = copyRealmEventsConfigRepresentation(rep);
// the "jboss-logging" listener should be enabled by default
assertTrue(rep.getEventsListeners().contains(JBossLoggingEventListenerProviderFactory.ID), "jboss-logging should be enabled initially");
// first modification => remove "event-queue", should be sent to the queue
rep.setEnabledEventTypes(List.of(EventType.LOGIN.name(), EventType.LOGIN_ERROR.name()));
rep.setEventsListeners(List.of(JBossLoggingEventListenerProviderFactory.ID));
rep.setEventsExpiration(36000L);
rep.setEventsEnabled(false);
rep.setAdminEventsEnabled(false);
rep.setAdminEventsDetailsEnabled(true);
managedRealm.admin().updateRealmEventsConfig(rep);
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, "events/config", rep, ResourceType.REALM);
RealmEventsConfigRepresentation actual = managedRealm.admin().getRealmEventsConfig();
checkRealmEventsConfigRepresentation(rep, actual);
// second modification => should not be sent cos event-queue was removed in the first mod
rep.setEnabledEventTypes(Arrays.asList(EventType.LOGIN.name(),
EventType.LOGIN_ERROR.name(), EventType.CLIENT_LOGIN.name()));
managedRealm.admin().updateRealmEventsConfig(rep);
Assertions.assertNull(adminEvents.poll());
actual = managedRealm.admin().getRealmEventsConfig();
checkRealmEventsConfigRepresentation(rep, actual);
// third modification => restore queue => should be sent and recovered
managedRealm.admin().updateRealmEventsConfig(repOrig);
AdminEventAssertion.assertEvent(adminEvents.poll(), OperationType.UPDATE, "events/config", repOrig, ResourceType.REALM);
actual = managedRealm.admin().getRealmEventsConfig();
checkRealmEventsConfigRepresentation(repOrig, actual);
}
@Test
public void updateRealmWithReservedCharInNameOrEmptyName() {
RealmRepresentation rep = managedRealm.admin().toRepresentation();
rep.setRealm("fo#o");
assertThrows(BadRequestException.class, () -> managedRealm.admin().update(rep));
rep.setRealm("");
assertThrows(BadRequestException.class, () -> managedRealm.admin().update(rep));
}
@Test
public void updateRealm() {
// first change
RealmRepresentation rep = managedRealm.admin().toRepresentation();
rep.setSsoSessionIdleTimeout(123);
rep.setSsoSessionMaxLifespan(12);
rep.setSsoSessionIdleTimeoutRememberMe(33);
rep.setSsoSessionMaxLifespanRememberMe(34);
rep.setAccessCodeLifespanLogin(1234);
rep.setActionTokenGeneratedByAdminLifespan(2345);
rep.setActionTokenGeneratedByUserLifespan(3456);
rep.setRegistrationAllowed(true);
rep.setRegistrationEmailAsUsername(true);
rep.setEditUsernameAllowed(true);
rep.setUserManagedAccessAllowed(true);
managedRealm.admin().update(rep);
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).representation(rep).resourceType(ResourceType.REALM);
rep = managedRealm.admin().toRepresentation();
assertEquals(123, rep.getSsoSessionIdleTimeout().intValue());
assertEquals(12, rep.getSsoSessionMaxLifespan().intValue());
assertEquals(33, rep.getSsoSessionIdleTimeoutRememberMe().intValue());
assertEquals(34, rep.getSsoSessionMaxLifespanRememberMe().intValue());
assertEquals(1234, rep.getAccessCodeLifespanLogin().intValue());
assertEquals(2345, rep.getActionTokenGeneratedByAdminLifespan().intValue());
assertEquals(3456, rep.getActionTokenGeneratedByUserLifespan().intValue());
assertEquals(Boolean.TRUE, rep.isRegistrationAllowed());
assertEquals(Boolean.TRUE, rep.isRegistrationEmailAsUsername());
assertEquals(Boolean.TRUE, rep.isEditUsernameAllowed());
assertEquals(Boolean.TRUE, rep.isUserManagedAccessAllowed());
// second change
rep.setRegistrationAllowed(false);
rep.setRegistrationEmailAsUsername(false);
rep.setEditUsernameAllowed(false);
rep.setUserManagedAccessAllowed(false);
managedRealm.admin().update(rep);
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).resourceType(ResourceType.REALM);
rep = managedRealm.admin().toRepresentation();
assertEquals(Boolean.FALSE, rep.isRegistrationAllowed());
assertEquals(Boolean.FALSE, rep.isRegistrationEmailAsUsername());
assertEquals(Boolean.FALSE, rep.isEditUsernameAllowed());
assertEquals(Boolean.FALSE, rep.isUserManagedAccessAllowed());
rep.setAccessCodeLifespanLogin(0);
rep.setAccessCodeLifespanUserAction(0);
try {
managedRealm.admin().update(rep);
Assertions.fail("Not expected to successfully update the realm");
} catch (Exception expected) {
// Expected exception
assertEquals("HTTP 400 Bad Request", expected.getMessage());
}
}
@Test
public void updateRealmWithNewRepresentation() {
// first change
RealmRepresentation rep = new RealmRepresentation();
rep.setEditUsernameAllowed(true);
rep.setSupportedLocales(new HashSet<>(Arrays.asList("en", "de")));
managedRealm.admin().update(rep);
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).resourceType(ResourceType.REALM);
rep = managedRealm.admin().toRepresentation();
assertEquals(Boolean.TRUE, rep.isEditUsernameAllowed());
assertEquals(2, rep.getSupportedLocales().size());
// second change
rep = new RealmRepresentation();
rep.setEditUsernameAllowed(false);
managedRealm.admin().update(rep);
AdminEventAssertion.assertSuccess(adminEvents.poll()).operationType(OperationType.UPDATE).resourceType(ResourceType.REALM);
rep = managedRealm.admin().toRepresentation();
assertEquals(Boolean.FALSE, rep.isEditUsernameAllowed());
assertEquals(2, rep.getSupportedLocales().size());
}
@Test
public void testNoUserProfileProviderComponentUponRealmChange() {
String realmName = "new-realm";
RealmRepresentation rep = new RealmRepresentation();
rep.setRealm(realmName);
adminClient.realms().create(rep);
assertThat(adminClient.realm(realmName).components().query(null, UserProfileProvider.class.getName()), empty());
rep.setDisplayName("displayName");
adminClient.realm(realmName).update(rep);
// this used to return non-empty collection
assertThat(adminClient.realm(realmName).components().query(null, UserProfileProvider.class.getName()), empty());
adminClient.realms().realm(realmName).remove();
}
private RealmEventsConfigRepresentation copyRealmEventsConfigRepresentation(RealmEventsConfigRepresentation rep) {
RealmEventsConfigRepresentation recr = new RealmEventsConfigRepresentation();
recr.setEnabledEventTypes(rep.getEnabledEventTypes());
recr.setEventsListeners(rep.getEventsListeners());
recr.setEventsExpiration(rep.getEventsExpiration());
recr.setEventsEnabled(rep.isEventsEnabled());
recr.setAdminEventsEnabled(rep.isAdminEventsEnabled());
recr.setAdminEventsDetailsEnabled(rep.isAdminEventsDetailsEnabled());
return recr;
}
private void checkRealmEventsConfigRepresentation(RealmEventsConfigRepresentation expected,
RealmEventsConfigRepresentation actual) {
assertEquals(expected.getEnabledEventTypes().size(), actual.getEnabledEventTypes().size());
assertTrue(actual.getEnabledEventTypes().containsAll(expected.getEnabledEventTypes()));
assertEquals(expected.getEventsListeners().size(), actual.getEventsListeners().size());
assertTrue(actual.getEventsListeners().containsAll(expected.getEventsListeners()));
assertEquals(expected.getEventsExpiration(), actual.getEventsExpiration());
assertEquals(expected.isEventsEnabled(), actual.isEventsEnabled());
assertEquals(expected.isAdminEventsEnabled(), actual.isAdminEventsEnabled());
assertEquals(expected.isAdminEventsDetailsEnabled(), actual.isAdminEventsDetailsEnabled());
}
}