Fixing realm removal when removing groups and brokers associated with an organization

Closes #29495

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2024-05-13 20:13:06 -03:00 committed by Alexander Schwartz
parent b5a854b68e
commit b4d231fd40
4 changed files with 80 additions and 6 deletions

View file

@ -78,6 +78,7 @@ import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.RealmLocalizationTextsEntity; import org.keycloak.models.jpa.entities.RealmLocalizationTextsEntity;
import org.keycloak.models.jpa.entities.RoleEntity; import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.organization.utils.Organizations;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
@ -190,7 +191,7 @@ public class JpaRealmProvider implements RealmProvider, ClientProvider, ClientSc
session.clientScopes().removeClientScopes(adapter); session.clientScopes().removeClientScopes(adapter);
session.roles().removeRoles(adapter); session.roles().removeRoles(adapter);
session.groups().getTopLevelGroupsStream(adapter).forEach(adapter::removeGroup); session.groups().getTopLevelGroupsStream(adapter).forEach(Organizations.removeGroup(session, adapter));
num = em.createNamedQuery("removeClientInitialAccessByRealm") num = em.createNamedQuery("removeClientInitialAccessByRealm")
.setParameter("realm", realm).executeUpdate(); .setParameter("realm", realm).executeUpdate();

View file

@ -113,14 +113,21 @@ public class JpaOrganizationProvider implements OrganizationProvider {
try { try {
session.setAttribute(OrganizationModel.class.getName(), organization); session.setAttribute(OrganizationModel.class.getName(), organization);
RealmModel realm = session.realms().getRealm(this.realm.getId());
// check if the realm is being removed so that we don't need to remove manually remove any other data but the org
if (realm != null) {
GroupModel group = getOrganizationGroup(entity); GroupModel group = getOrganizationGroup(entity);
if (group != null) { if (group != null) {
//TODO: won't scale, requires a better mechanism for bulk deleting users //TODO: won't scale, requires a better mechanism for bulk deleting users
userProvider.getGroupMembersStream(realm, group).forEach(userModel -> removeMember(organization, userModel)); userProvider.getGroupMembersStream(this.realm, group).forEach(userModel -> removeMember(organization, userModel));
groupProvider.removeGroup(realm, group); groupProvider.removeGroup(this.realm, group);
}
organization.getIdentityProviders().forEach((model) -> removeIdentityProvider(organization, model)); organization.getIdentityProviders().forEach((model) -> removeIdentityProvider(organization, model));
} }
em.remove(entity); em.remove(entity);
} finally { } finally {
session.removeAttribute(OrganizationModel.class.getName()); session.removeAttribute(OrganizationModel.class.getName());

View file

@ -19,6 +19,7 @@ package org.keycloak.organization.utils;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
import org.keycloak.common.Profile; import org.keycloak.common.Profile;
import org.keycloak.common.Profile.Feature; import org.keycloak.common.Profile.Feature;
@ -85,4 +86,32 @@ public class Organizations {
return null; return null;
} }
public static Consumer<GroupModel> removeGroup(KeycloakSession session, RealmModel realm) {
return group -> {
if (!Profile.isFeatureEnabled(Feature.ORGANIZATION)) {
realm.removeGroup(group);
return;
}
OrganizationModel current = (OrganizationModel) session.getAttribute(OrganizationModel.class.getName());
try {
String orgId = group.getFirstAttribute(OrganizationModel.ORGANIZATION_ATTRIBUTE);
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
if (orgId != null) {
session.setAttribute(OrganizationModel.class.getName(), provider.getById(orgId));
}
realm.removeGroup(group);
} finally {
if (current == null) {
session.removeAttribute(OrganizationModel.class.getName());
} else {
session.setAttribute(OrganizationModel.class.getName(), current);
}
}
};
}
} }

View file

@ -43,10 +43,15 @@ import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.Response.Status;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.OrganizationResource; import org.keycloak.admin.client.resource.OrganizationResource;
import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.common.Profile.Feature; import org.keycloak.common.Profile.Feature;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.representations.idm.OrganizationDomainRepresentation; import org.keycloak.representations.idm.OrganizationDomainRepresentation;
import org.keycloak.representations.idm.OrganizationRepresentation; import org.keycloak.representations.idm.OrganizationRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature; import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.util.RealmBuilder;
@EnableFeature(Feature.ORGANIZATION) @EnableFeature(Feature.ORGANIZATION)
public class OrganizationTest extends AbstractOrganizationTest { public class OrganizationTest extends AbstractOrganizationTest {
@ -367,4 +372,36 @@ public class OrganizationTest extends AbstractOrganizationTest {
assertEquals(1, existing.getDomains().size()); assertEquals(1, existing.getDomains().size());
assertNotNull(existing.getDomain("acme.com")); assertNotNull(existing.getDomain("acme.com"));
} }
@Test
public void testDeleteRealm() {
RealmRepresentation realmRep = RealmBuilder.create().name(KeycloakModelUtils.generateId()).build();
RealmResource realm = realmsResouce().realm(realmRep.getRealm());
try {
realmRep.setEnabled(true);
realmsResouce().create(realmRep);
realm = realmsResouce().realm(realmRep.getRealm());
realm.toRepresentation();
OrganizationRepresentation org = new OrganizationRepresentation();
org.setName("test-org");
org.addDomain(new OrganizationDomainRepresentation("test.org"));
org.setEnabled(true);
Response response = realm.organizations().create(org);
response.close();
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
List<OrganizationRepresentation> orgs = realm.organizations().getAll();
assertEquals(1, orgs.size());
IdentityProviderRepresentation broker = bc.setUpIdentityProvider();
broker.setAlias(KeycloakModelUtils.generateId());
response = realm.identityProviders().create(broker);
response.close();
assertEquals(Status.CREATED.getStatusCode(), response.getStatus());
response = realm.organizations().get(orgs.get(0).getId()).identityProviders().addIdentityProvider(broker.getAlias());
response.close();
assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
} finally {
realm.remove();
}
}
} }