Invalidating domain cache and introducing cache for more query methods
Signed-off-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
1f8280c71a
commit
04bd6653ec
8 changed files with 417 additions and 43 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ import java.util.Map;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.keycloak.models.IdentityProviderModel;
|
import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.MembershipMetadata;
|
import org.keycloak.models.OrganizationDomainModel;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -56,7 +56,7 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean remove(OrganizationModel organization) {
|
public boolean remove(OrganizationModel organization) {
|
||||||
registerOrganizationInvalidation(organization.getId());
|
registerOrganizationInvalidation(organization);
|
||||||
registerCountInvalidation();
|
registerCountInvalidation();
|
||||||
return orgDelegate.remove(organization);
|
return orgDelegate.remove(organization);
|
||||||
}
|
}
|
||||||
|
@ -73,47 +73,42 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
Long loaded = realmCache.getCache().getCurrentRevision(id);
|
Long loaded = realmCache.getCache().getCurrentRevision(id);
|
||||||
OrganizationModel model = orgDelegate.getById(id);
|
OrganizationModel model = orgDelegate.getById(id);
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
if (realmCache.getInvalidations().contains(id)) return model;
|
if (isInvalid(id)) return model;
|
||||||
cached = new CachedOrganization(loaded, getRealm(), model);
|
cached = new CachedOrganization(loaded, getRealm(), model);
|
||||||
realmCache.getCache().addRevisioned(cached, realmCache.getStartupRevision());
|
realmCache.getCache().addRevisioned(cached, realmCache.getStartupRevision());
|
||||||
|
|
||||||
// no need to check for realm invalidation as IdP changes are handled by events within InfinispanOrganizationProviderFactory
|
// 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);
|
return orgDelegate.getById(id);
|
||||||
} else if (managedOrganizations.containsKey(id)) {
|
} else if (managedOrganizations.containsKey(id)) {
|
||||||
return managedOrganizations.get(id);
|
return managedOrganizations.get(id);
|
||||||
}
|
}
|
||||||
OrganizationAdapter adapter = new OrganizationAdapter(cached, realmCache, orgDelegate);
|
OrganizationAdapter adapter = new OrganizationAdapter(cached, realmCache, orgDelegate, this);
|
||||||
managedOrganizations.put(id, adapter);
|
managedOrganizations.put(id, adapter);
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OrganizationModel getByDomainName(String domainName) {
|
public OrganizationModel getByDomainName(String domainName) {
|
||||||
String cacheKey = getRealm().getId() + "+.org.domain.name." + domainName;
|
String cacheKey = cacheKeyByDomain(domainName);
|
||||||
CachedOrganization cached = realmCache.getCache().get(cacheKey, CachedOrganization.class);
|
|
||||||
String realmId = getRealm().getId();
|
if (isInvalid(cacheKey)) {
|
||||||
if (cached != null && !cached.getRealm().equals(realmId)) {
|
return orgDelegate.getByDomainName(domainName);
|
||||||
cached = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CachedOrganizationIds cached = realmCache.getCache().get(cacheKey, CachedOrganizationIds.class);
|
||||||
|
|
||||||
if (cached == null) {
|
if (cached == null) {
|
||||||
Long loaded = realmCache.getCache().getCurrentRevision(cacheKey);
|
Long loaded = realmCache.getCache().getCurrentRevision(cacheKey);
|
||||||
OrganizationModel model = orgDelegate.getByDomainName(domainName);
|
OrganizationModel model = orgDelegate.getByDomainName(domainName);
|
||||||
if (model == null) return null;
|
if (model == null) {
|
||||||
if (realmCache.getInvalidations().contains(model.getId())) return model;
|
return null;
|
||||||
cached = new CachedOrganization(loaded, getRealm(), model);
|
}
|
||||||
|
cached = new CachedOrganizationIds(loaded, cacheKey, getRealm(), Stream.of(model));
|
||||||
realmCache.getCache().addRevisioned(cached, realmCache.getStartupRevision());
|
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 cached.getOrgIds().stream().map(this::getById).findAny().orElse(null);
|
||||||
return adapter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -137,16 +132,19 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addManagedMember(OrganizationModel organization, UserModel user) {
|
public boolean addManagedMember(OrganizationModel organization, UserModel user) {
|
||||||
|
registerMemberInvalidation(organization, user);
|
||||||
return orgDelegate.addManagedMember(organization, user);
|
return orgDelegate.addManagedMember(organization, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean addMember(OrganizationModel organization, UserModel user) {
|
public boolean addMember(OrganizationModel organization, UserModel user) {
|
||||||
|
registerMemberInvalidation(organization, user);
|
||||||
return orgDelegate.addMember(organization, user);
|
return orgDelegate.addMember(organization, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeMember(OrganizationModel organization, UserModel member) {
|
public boolean removeMember(OrganizationModel organization, UserModel member) {
|
||||||
|
registerMemberInvalidation(organization, member);
|
||||||
return orgDelegate.removeMember(organization, member);
|
return orgDelegate.removeMember(organization, member);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,24 +155,76 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getMemberById(OrganizationModel organization, String id) {
|
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
|
@Override
|
||||||
public Stream<OrganizationModel> getByMember(UserModel member) {
|
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
|
@Override
|
||||||
public boolean isManagedMember(OrganizationModel organization, UserModel member) {
|
public boolean isManagedMember(OrganizationModel organization, UserModel user) {
|
||||||
return orgDelegate.isManagedMember(organization, member);
|
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
|
@Override
|
||||||
public boolean addIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider) {
|
public boolean addIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider) {
|
||||||
boolean added = orgDelegate.addIdentityProvider(organization, identityProvider);
|
boolean added = orgDelegate.addIdentityProvider(organization, identityProvider);
|
||||||
if (added) {
|
if (added) {
|
||||||
registerOrganizationInvalidation(organization.getId());
|
registerOrganizationInvalidation(organization);
|
||||||
}
|
}
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +238,7 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
public boolean removeIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider) {
|
public boolean removeIdentityProvider(OrganizationModel organization, IdentityProviderModel identityProvider) {
|
||||||
boolean removed = orgDelegate.removeIdentityProvider(organization, identityProvider);
|
boolean removed = orgDelegate.removeIdentityProvider(organization, identityProvider);
|
||||||
if (removed) {
|
if (removed) {
|
||||||
registerOrganizationInvalidation(organization.getId());
|
registerOrganizationInvalidation(organization);
|
||||||
}
|
}
|
||||||
return removed;
|
return removed;
|
||||||
}
|
}
|
||||||
|
@ -204,7 +254,7 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
CachedOrganizationCount cached = realmCache.getCache().get(cacheKey, CachedOrganizationCount.class);
|
CachedOrganizationCount cached = realmCache.getCache().get(cacheKey, CachedOrganizationCount.class);
|
||||||
|
|
||||||
// cached and not invalidated
|
// cached and not invalidated
|
||||||
if (cached != null && !realmCache.getInvalidations().contains(cacheKey)) {
|
if (cached != null && !isInvalid(cacheKey)) {
|
||||||
return cached.getCount();
|
return cached.getCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -221,13 +271,20 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
orgDelegate.close();
|
orgDelegate.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerOrganizationInvalidation(String orgId) {
|
void registerOrganizationInvalidation(OrganizationModel organization) {
|
||||||
OrganizationAdapter adapter = managedOrganizations.get(orgId);
|
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) {
|
if (adapter != null) {
|
||||||
adapter.invalidate();
|
adapter.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
realmCache.registerInvalidation(orgId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerCountInvalidation() {
|
private void registerCountInvalidation() {
|
||||||
|
@ -245,4 +302,25 @@ public class InfinispanOrganizationProvider implements OrganizationProvider {
|
||||||
private Stream<OrganizationModel> getCacheDelegates(Stream<OrganizationModel> backendOrganizations) {
|
private Stream<OrganizationModel> getCacheDelegates(Stream<OrganizationModel> backendOrganizations) {
|
||||||
return backendOrganizations.map(OrganizationModel::getId).map(this::getById);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.organization.OrganizationProvider;
|
import org.keycloak.organization.OrganizationProvider;
|
||||||
import org.keycloak.models.OrganizationModel;
|
import org.keycloak.models.OrganizationModel;
|
||||||
import org.keycloak.organization.OrganizationProviderFactory;
|
import org.keycloak.organization.OrganizationProviderFactory;
|
||||||
|
@ -41,12 +42,17 @@ public class InfinispanOrganizationProviderFactory implements OrganizationProvid
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
factory.register(event -> {
|
factory.register(e -> {
|
||||||
if (event instanceof RealmModel.IdentityProviderUpdatedEvent idpUpdatedEvent) {
|
if (e instanceof RealmModel.IdentityProviderUpdatedEvent event) {
|
||||||
registerOrganizationInvalidation(idpUpdatedEvent.getKeycloakSession(), idpUpdatedEvent.getUpdatedIdentityProvider());
|
registerOrganizationInvalidation(event.getKeycloakSession(), event.getUpdatedIdentityProvider());
|
||||||
}
|
}
|
||||||
if (event instanceof RealmModel.IdentityProviderRemovedEvent idpRemovedEvent) {
|
if (e instanceof RealmModel.IdentityProviderRemovedEvent event) {
|
||||||
registerOrganizationInvalidation(idpRemovedEvent.getKeycloakSession(), idpRemovedEvent.getRemovedIdentityProvider());
|
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) {
|
if (idp.getConfig().get(OrganizationModel.ORGANIZATION_ATTRIBUTE) != null) {
|
||||||
InfinispanOrganizationProvider orgProvider = (InfinispanOrganizationProvider) session.getProvider(OrganizationProvider.class, getId());
|
InfinispanOrganizationProvider orgProvider = (InfinispanOrganizationProvider) session.getProvider(OrganizationProvider.class, getId());
|
||||||
if (orgProvider != null) {
|
if (orgProvider != null) {
|
||||||
orgProvider.registerOrganizationInvalidation(idp.getConfig().get(OrganizationModel.ORGANIZATION_ATTRIBUTE));
|
OrganizationModel organization = orgProvider.getById(idp.getConfig().get(OrganizationModel.ORGANIZATION_ATTRIBUTE));
|
||||||
|
orgProvider.registerOrganizationInvalidation(organization);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,13 @@ public class OrganizationAdapter implements OrganizationModel {
|
||||||
private final CacheRealmProvider realmCache;
|
private final CacheRealmProvider realmCache;
|
||||||
private final CachedOrganization cached;
|
private final CachedOrganization cached;
|
||||||
private final OrganizationProvider delegate;
|
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.cached = cached;
|
||||||
this.realmCache = realmCache;
|
this.realmCache = realmCache;
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
|
this.organizationCache = organizationCache;
|
||||||
this.modelSupplier = this::getOrganizationModel;
|
this.modelSupplier = this::getOrganizationModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,8 +64,8 @@ public class OrganizationAdapter implements OrganizationModel {
|
||||||
|
|
||||||
private void getDelegateForUpdate() {
|
private void getDelegateForUpdate() {
|
||||||
if (updated == null) {
|
if (updated == null) {
|
||||||
realmCache.registerInvalidation(cached.getId());
|
|
||||||
updated = modelSupplier.get();
|
updated = modelSupplier.get();
|
||||||
|
organizationCache.registerOrganizationInvalidation(updated);
|
||||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,8 +125,9 @@ public class JpaOrganizationProvider implements OrganizationProvider {
|
||||||
GroupModel group = getOrganizationGroup(entity);
|
GroupModel group = getOrganizationGroup(entity);
|
||||||
|
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
|
OrganizationProvider provider = session.getProvider(OrganizationProvider.class);
|
||||||
//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(realm, group).forEach(userModel -> provider.removeMember(organization, userModel));
|
||||||
groupProvider.removeGroup(realm, group);
|
groupProvider.removeGroup(realm, group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -417,7 +417,6 @@ public abstract class AbstractBrokerSelfRegistrationTest extends AbstractOrganiz
|
||||||
List<FederatedIdentityRepresentation> federatedIdentities = testRealm().users().get(user.getId()).getFederatedIdentity();
|
List<FederatedIdentityRepresentation> federatedIdentities = testRealm().users().get(user.getId()).getFederatedIdentity();
|
||||||
assertEquals(1, federatedIdentities.size());
|
assertEquals(1, federatedIdentities.size());
|
||||||
assertEquals(bc.getIDPAlias(), federatedIdentities.get(0).getIdentityProvider());
|
assertEquals(bc.getIDPAlias(), federatedIdentities.get(0).getIdentityProvider());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -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());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue