Invalidating domain cache and introducing cache for more query methods

Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
Pedro Igor 2024-07-18 21:21:50 -03:00 committed by Michal Hajas
parent 1f8280c71a
commit 04bd6653ec
8 changed files with 417 additions and 43 deletions

View file

@ -0,0 +1,49 @@
/*
* Copyright 2024 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.models.cache.infinispan.organization;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
import org.keycloak.models.cache.infinispan.entities.InRealm;
public class CachedMembership extends AbstractRevisioned implements InRealm {
private final RealmModel realm;
private final boolean managed;
private final boolean isMember;
public CachedMembership(Long revision, String key, RealmModel realm, boolean managed, boolean isMember) {
super(revision, key);
this.realm = realm;
this.managed = managed;
this.isMember = isMember;
}
@Override
public String getRealm() {
return realm.getId();
}
public boolean isManaged() {
return managed;
}
public boolean isMember() {
return isMember;
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright 2024 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.models.cache.infinispan.organization;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
import org.keycloak.models.cache.infinispan.entities.InRealm;
public class CachedOrganizationIds extends AbstractRevisioned implements InRealm {
private final RealmModel realm;
private final Set<String> orgIds = new HashSet<>();
public CachedOrganizationIds(Long revision, String id, RealmModel realm, Stream<OrganizationModel> organizations) {
super(revision, id);
this.realm = realm;
organizations.map(OrganizationModel::getId).forEach(orgIds::add);
}
@Override
public String getRealm() {
return realm.getId();
}
public Set<String> getOrgIds() {
return orgIds;
}
}

View file

@ -21,7 +21,7 @@ import java.util.Map;
import java.util.stream.Stream;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.MembershipMetadata;
import org.keycloak.models.OrganizationDomainModel;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
@ -56,7 +56,7 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
@Override
public boolean remove(OrganizationModel organization) {
registerOrganizationInvalidation(organization.getId());
registerOrganizationInvalidation(organization);
registerCountInvalidation();
return orgDelegate.remove(organization);
}
@ -73,47 +73,42 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
Long loaded = realmCache.getCache().getCurrentRevision(id);
OrganizationModel model = orgDelegate.getById(id);
if (model == null) return null;
if (realmCache.getInvalidations().contains(id)) return model;
if (isInvalid(id)) return model;
cached = new CachedOrganization(loaded, getRealm(), model);
realmCache.getCache().addRevisioned(cached, realmCache.getStartupRevision());
// no need to check for realm invalidation as IdP changes are handled by events within InfinispanOrganizationProviderFactory
} else if (realmCache.getInvalidations().contains(id)) {
} else if (isInvalid(id)) {
return orgDelegate.getById(id);
} else if (managedOrganizations.containsKey(id)) {
return managedOrganizations.get(id);
}
OrganizationAdapter adapter = new OrganizationAdapter(cached, realmCache, orgDelegate);
OrganizationAdapter adapter = new OrganizationAdapter(cached, realmCache, orgDelegate, this);
managedOrganizations.put(id, adapter);
return adapter;
}
@Override
public OrganizationModel getByDomainName(String domainName) {
String cacheKey = getRealm().getId() + "+.org.domain.name." + domainName;
CachedOrganization cached = realmCache.getCache().get(cacheKey, CachedOrganization.class);
String realmId = getRealm().getId();
if (cached != null && !cached.getRealm().equals(realmId)) {
cached = null;
String cacheKey = cacheKeyByDomain(domainName);
if (isInvalid(cacheKey)) {
return orgDelegate.getByDomainName(domainName);
}
CachedOrganizationIds cached = realmCache.getCache().get(cacheKey, CachedOrganizationIds.class);
if (cached == null) {
Long loaded = realmCache.getCache().getCurrentRevision(cacheKey);
OrganizationModel model = orgDelegate.getByDomainName(domainName);
if (model == null) return null;
if (realmCache.getInvalidations().contains(model.getId())) return model;
cached = new CachedOrganization(loaded, getRealm(), model);
if (model == null) {
return null;
}
cached = new CachedOrganizationIds(loaded, cacheKey, getRealm(), Stream.of(model));
realmCache.getCache().addRevisioned(cached, realmCache.getStartupRevision());
// no need to check for realm invalidation as IdP changes are handled by events within InfinispanOrganizationProviderFactory
} else if (realmCache.getInvalidations().contains(cached.getId())) {
return orgDelegate.getByDomainName(domainName);
} else if (managedOrganizations.containsKey(cached.getId())) {
return managedOrganizations.get(cached.getId());
}
OrganizationAdapter adapter = new OrganizationAdapter(cached, realmCache, orgDelegate);
managedOrganizations.put(cacheKey, adapter);
return adapter;
return cached.getOrgIds().stream().map(this::getById).findAny().orElse(null);
}
@Override
@ -137,16 +132,19 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
@Override
public boolean addManagedMember(OrganizationModel organization, UserModel user) {
registerMemberInvalidation(organization, user);
return orgDelegate.addManagedMember(organization, user);
}
@Override
public boolean addMember(OrganizationModel organization, UserModel user) {
registerMemberInvalidation(organization, user);
return orgDelegate.addMember(organization, user);
}
@Override
public boolean removeMember(OrganizationModel organization, UserModel member) {
registerMemberInvalidation(organization, member);
return orgDelegate.removeMember(organization, member);
}
@ -157,24 +155,76 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
@Override
public UserModel getMemberById(OrganizationModel organization, String id) {
return orgDelegate.getMemberById(organization, id);
RealmModel realm = getRealm();
UserModel user = session.users().getUserById(realm, id);
if (user == null) {
return null;
}
String cacheKey = cacheKeyMembership(realm, organization, user);
if (isInvalid(cacheKey)) {
return orgDelegate.getMemberById(organization, user.getId());
}
CachedMembership cached = realmCache.getCache().get(cacheKey, CachedMembership.class);
if (cached == null) {
boolean isManaged = orgDelegate.isManagedMember(organization, user);
Long loaded = realmCache.getCache().getCurrentRevision(cacheKey);
UserModel member = orgDelegate.getMemberById(organization, user.getId());
cached = new CachedMembership(loaded, cacheKeyMembership(realm, organization, user), realm, isManaged, member != null);
realmCache.getCache().addRevisioned(cached, realmCache.getStartupRevision());
}
return cached.isMember() ? user : null;
}
@Override
public Stream<OrganizationModel> getByMember(UserModel member) {
return orgDelegate.getByMember(member);
String cacheKey = cacheKeyByMember(member);
if (isInvalid(cacheKey)) {
return orgDelegate.getByMember(member);
}
CachedOrganizationIds cached = realmCache.getCache().get(cacheKey, CachedOrganizationIds.class);
if (cached == null) {
Long loaded = realmCache.getCache().getCurrentRevision(cacheKey);
Stream<OrganizationModel> model = orgDelegate.getByMember(member);
cached = new CachedOrganizationIds(loaded, cacheKey, getRealm(), model);
realmCache.getCache().addRevisioned(cached, realmCache.getStartupRevision());
}
return cached.getOrgIds().stream().map(this::getById);
}
@Override
public boolean isManagedMember(OrganizationModel organization, UserModel member) {
return orgDelegate.isManagedMember(organization, member);
public boolean isManagedMember(OrganizationModel organization, UserModel user) {
UserModel member = getMemberById(organization, user.getId());
if (member == null) {
return false;
}
String cacheKey = cacheKeyMembership(getRealm(), organization, member);
CachedMembership cached = realmCache.getCache().get(cacheKey, CachedMembership.class);
if (cached == null) {
return orgDelegate.isManagedMember(organization, user);
}
return cached.isManaged();
}
@Override
public boolean addIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider) {
boolean added = orgDelegate.addIdentityProvider(organization, identityProvider);
if (added) {
registerOrganizationInvalidation(organization.getId());
registerOrganizationInvalidation(organization);
}
return added;
}
@ -188,7 +238,7 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
public boolean removeIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider) {
boolean removed = orgDelegate.removeIdentityProvider(organization, identityProvider);
if (removed) {
registerOrganizationInvalidation(organization.getId());
registerOrganizationInvalidation(organization);
}
return removed;
}
@ -204,7 +254,7 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
CachedOrganizationCount cached = realmCache.getCache().get(cacheKey, CachedOrganizationCount.class);
// cached and not invalidated
if (cached != null && !realmCache.getInvalidations().contains(cacheKey)) {
if (cached != null && !isInvalid(cacheKey)) {
return cached.getCount();
}
@ -221,13 +271,20 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
orgDelegate.close();
}
void registerOrganizationInvalidation(String orgId) {
OrganizationAdapter adapter = managedOrganizations.get(orgId);
void registerOrganizationInvalidation(OrganizationModel organization) {
String id = organization.getId();
realmCache.registerInvalidation(id);
organization.getDomains()
.map(OrganizationDomainModel::getName)
.map(this::cacheKeyByDomain)
.forEach(realmCache::registerInvalidation);
OrganizationAdapter adapter = managedOrganizations.get(id);
if (adapter != null) {
adapter.invalidate();
}
realmCache.registerInvalidation(orgId);
}
private void registerCountInvalidation() {
@ -245,4 +302,25 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
private Stream<OrganizationModel> getCacheDelegates(Stream<OrganizationModel> backendOrganizations) {
return backendOrganizations.map(OrganizationModel::getId).map(this::getById);
}
private String cacheKeyByDomain(String domainName) {
return getRealm().getId() + ".org.domain.name." + domainName;
}
private String cacheKeyByMember(UserModel user) {
return getRealm().getId() + ".org.member." + user.getId() + ".orgs";
}
private String cacheKeyMembership(RealmModel realm, OrganizationModel organization, UserModel user) {
return realm.getId() + ".org." + organization.getId() + ".member." + user.getId() + ".membership";
}
void registerMemberInvalidation(OrganizationModel organization, UserModel member) {
realmCache.registerInvalidation(cacheKeyByMember(member));
realmCache.registerInvalidation(cacheKeyMembership(getRealm(), organization, member));
}
private boolean isInvalid(String cacheKey) {
return realmCache.getInvalidations().contains(cacheKey);
}
}

View file

@ -22,6 +22,7 @@ import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.organization.OrganizationProvider;
import org.keycloak.models.OrganizationModel;
import org.keycloak.organization.OrganizationProviderFactory;
@ -41,12 +42,17 @@ public class InfinispanOrganizationProviderFactory implements OrganizationProvid
@Override
public void postInit(KeycloakSessionFactory factory) {
factory.register(event -> {
if (event instanceof RealmModel.IdentityProviderUpdatedEvent idpUpdatedEvent) {
registerOrganizationInvalidation(idpUpdatedEvent.getKeycloakSession(), idpUpdatedEvent.getUpdatedIdentityProvider());
factory.register(e -> {
if (e instanceof RealmModel.IdentityProviderUpdatedEvent event) {
registerOrganizationInvalidation(event.getKeycloakSession(), event.getUpdatedIdentityProvider());
}
if (event instanceof RealmModel.IdentityProviderRemovedEvent idpRemovedEvent) {
registerOrganizationInvalidation(idpRemovedEvent.getKeycloakSession(), idpRemovedEvent.getRemovedIdentityProvider());
if (e instanceof RealmModel.IdentityProviderRemovedEvent event) {
registerOrganizationInvalidation(event.getKeycloakSession(), event.getRemovedIdentityProvider());
}
if (e instanceof UserModel.UserRemovedEvent event) {
KeycloakSession session = event.getKeycloakSession();
InfinispanOrganizationProvider orgProvider = (InfinispanOrganizationProvider) session.getProvider(OrganizationProvider.class, getId());
orgProvider.getByMember(event.getUser()).forEach(organization -> orgProvider.registerMemberInvalidation(organization, event.getUser()));
}
});
}
@ -55,7 +61,8 @@ public class InfinispanOrganizationProviderFactory implements OrganizationProvid
if (idp.getConfig().get(OrganizationModel.ORGANIZATION_ATTRIBUTE) != null) {
InfinispanOrganizationProvider orgProvider = (InfinispanOrganizationProvider) session.getProvider(OrganizationProvider.class, getId());
if (orgProvider != null) {
orgProvider.registerOrganizationInvalidation(idp.getConfig().get(OrganizationModel.ORGANIZATION_ATTRIBUTE));
OrganizationModel organization = orgProvider.getById(idp.getConfig().get(OrganizationModel.ORGANIZATION_ATTRIBUTE));
orgProvider.registerOrganizationInvalidation(organization);
}
}
}

View file

@ -36,11 +36,13 @@ public class OrganizationAdapter implements OrganizationModel {
private final CacheRealmProvider realmCache;
private final CachedOrganization cached;
private final OrganizationProvider delegate;
private final InfinispanOrganizationProvider organizationCache;
public OrganizationAdapter(CachedOrganization cached, CacheRealmProvider realmCache, OrganizationProvider delegate) {
public OrganizationAdapter(CachedOrganization cached, CacheRealmProvider realmCache, OrganizationProvider delegate, InfinispanOrganizationProvider organizationCache) {
this.cached = cached;
this.realmCache = realmCache;
this.delegate = delegate;
this.organizationCache = organizationCache;
this.modelSupplier = this::getOrganizationModel;
}
@ -62,8 +64,8 @@ public class OrganizationAdapter implements OrganizationModel {
private void getDelegateForUpdate() {
if (updated == null) {
realmCache.registerInvalidation(cached.getId());
updated = modelSupplier.get();
organizationCache.registerOrganizationInvalidation(updated);
if (updated == null) throw new IllegalStateException("Not found in database");
}
}

View file

@ -125,8 +125,9 @@ public class JpaOrganizationProvider implements OrganizationProvider {
GroupModel group = getOrganizationGroup(entity);
if (group != null) {
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
//TODO: won't scale, requires a better mechanism for bulk deleting users
userProvider.getGroupMembersStream(realm, group).forEach(userModel -> removeMember(organization, userModel));
userProvider.getGroupMembersStream(realm, group).forEach(userModel -> provider.removeMember(organization, userModel));
groupProvider.removeGroup(realm, group);
}

View file

@ -417,7 +417,6 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
List<FederatedIdentityRepresentation> federatedIdentities = testRealm().users().get(user.getId()).getFederatedIdentity();
assertEquals(1, federatedIdentities.size());
assertEquals(bc.getIDPAlias(), federatedIdentities.get(0).getIdentityProvider());
}
@Test

View file

@ -0,0 +1,190 @@
/*
* Copyright 2024 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.testsuite.organization.cache;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.common.Profile.Feature;
import org.keycloak.models.OrganizationDomainModel;
import org.keycloak.models.OrganizationModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.organization.OrganizationProvider;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.organization.admin.AbstractOrganizationTest;
import org.keycloak.testsuite.runonserver.RunOnServer;
@EnableFeature(Feature.ORGANIZATION)
public class OrganizationCacheTest extends AbstractOrganizationTest {
@Before
public void onBefore() {
createOrganization("orga");
createOrganization("orgb");
}
@After
public void onAfter() {
List<UserRepresentation> users = testRealm().users().search("member");
if (!users.isEmpty()) {
UserRepresentation member = users.get(0);
testRealm().users().get(member.getId()).remove();
}
}
@Test
public void testGetByDomain() {
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel acme = orgProvider.getByDomainName("orga.org");
assertNotNull(acme);
acme.setDomains(Set.of(new OrganizationDomainModel("acme.org")));
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel acme = orgProvider.getByDomainName("orga.org");
assertNull(acme);
acme = orgProvider.getByDomainName("acme.org");
assertNotNull(acme);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel acme = orgProvider.getByDomainName("acme.org");
assertNotNull(acme);
orgProvider.remove(acme);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel acme = orgProvider.getByDomainName("acme.org");
assertNull(acme);
});
}
@Test
public void testGetByMember() {
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel orga = orgProvider.getByDomainName("orga.org");
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().addUser(realm, "member");
member.setEnabled(true);
orgProvider.addMember(orga, member);
OrganizationModel orgb = orgProvider.getByDomainName("orgb.org");
orgProvider.addMember(orgb, member);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
Stream<OrganizationModel> memberOf = orgProvider.getByMember(member);
assertEquals(2, memberOf.count());
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel orga = orgProvider.getByDomainName("orga.org");
orgProvider.remove(orga);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
Stream<OrganizationModel> memberOf = orgProvider.getByMember(member);
assertEquals(1, memberOf.count());
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
OrganizationModel orgb = orgProvider.getByDomainName("orgb.org");
orgProvider.removeMember(orgb, member);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
Stream<OrganizationModel> memberOf = orgProvider.getByMember(member);
assertEquals(0, memberOf.count());
});
}
@Test
public void testGetByMemberId() {
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel orga = orgProvider.getByDomainName("orga.org");
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().addUser(realm, "member");
member.setEnabled(true);
orgProvider.addMember(orga, member);
OrganizationModel orgb = orgProvider.getByDomainName("orgb.org");
orgProvider.addMember(orgb, member);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel org = orgProvider.getByDomainName("orga.org");
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
UserModel memberOf = orgProvider.getMemberById(org, member.getId());
assertNotNull(memberOf);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel org = orgProvider.getByDomainName("orga.org");
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
orgProvider.removeMember(org, member);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel orga = orgProvider.getByDomainName("orga.org");
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
assertNull(orgProvider.getMemberById(orga, member.getId()));
OrganizationModel orgb = orgProvider.getByDomainName("orgb.org");
assertNotNull(orgProvider.getMemberById(orgb, member.getId()));
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
OrganizationModel orgb = orgProvider.getByDomainName("orgb.org");
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
assertEquals(1, orgProvider.getByMember(member).count());
orgProvider.remove(orgb);
});
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) session -> {
OrganizationProvider orgProvider = session.getProvider(OrganizationProvider.class);
RealmModel realm = session.getContext().getRealm();
UserModel member = session.users().getUserByUsername(realm, "member");
assertEquals(0, orgProvider.getByMember(member).count());
});
}
}