Merge pull request #2413 from mposolda/master
Caching of identityProvider links
This commit is contained in:
commit
0dea5eac46
2 changed files with 155 additions and 4 deletions
|
@ -33,6 +33,7 @@ import org.keycloak.models.UserFederationProviderModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserProvider;
|
||||
import org.keycloak.models.cache.CacheUserProvider;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedFederatedIdentityLinks;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedUser;
|
||||
import org.keycloak.models.cache.infinispan.entities.UserListQuery;
|
||||
|
||||
|
@ -81,6 +82,7 @@ public class UserCacheSession implements CacheUserProvider {
|
|||
invalidations.add(user.getId());
|
||||
if (user.getEmail() != null) invalidations.add(getUserByEmailCacheKey(realm.getId(), user.getEmail()));
|
||||
invalidations.add(getUserByUsernameCacheKey(realm.getId(), user.getUsername()));
|
||||
if (realm.isIdentityFederationEnabled()) invalidations.add(getFederatedIdentityLinksCacheKey(user.getId()));
|
||||
}
|
||||
|
||||
protected void runInvalidations() {
|
||||
|
@ -176,6 +178,14 @@ public class UserCacheSession implements CacheUserProvider {
|
|||
return realmId + ".email." + email;
|
||||
}
|
||||
|
||||
public String getUserByFederatedIdentityCacheKey(String realmId, FederatedIdentityModel socialLink) {
|
||||
return realmId + ".idp." + socialLink.getIdentityProvider() + "." + socialLink.getUserId();
|
||||
}
|
||||
|
||||
public String getFederatedIdentityLinksCacheKey(String userId) {
|
||||
return userId + ".idplinks";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
||||
logger.tracev("getUserByUsername: {0}", username);
|
||||
|
@ -279,7 +289,46 @@ public class UserCacheSession implements CacheUserProvider {
|
|||
|
||||
@Override
|
||||
public UserModel getUserByFederatedIdentity(FederatedIdentityModel socialLink, RealmModel realm) {
|
||||
return getDelegate().getUserByFederatedIdentity(socialLink, realm);
|
||||
if (socialLink == null) return null;
|
||||
if (!realm.isIdentityFederationEnabled()) return null;
|
||||
|
||||
if (realmInvalidations.contains(realm.getId())) {
|
||||
return getDelegate().getUserByFederatedIdentity(socialLink, realm);
|
||||
}
|
||||
String cacheKey = getUserByFederatedIdentityCacheKey(realm.getId(), socialLink);
|
||||
if (invalidations.contains(cacheKey)) {
|
||||
return getDelegate().getUserByFederatedIdentity(socialLink, realm);
|
||||
}
|
||||
UserListQuery query = cache.get(cacheKey, UserListQuery.class);
|
||||
|
||||
String userId = null;
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
UserModel model = getDelegate().getUserByFederatedIdentity(socialLink, realm);
|
||||
if (model == null) return null;
|
||||
userId = model.getId();
|
||||
query = new UserListQuery(loaded, cacheKey, realm, userId);
|
||||
cache.addRevisioned(query, startupRevision);
|
||||
if (invalidations.contains(userId)) return model;
|
||||
if (managedUsers.containsKey(userId)) return managedUsers.get(userId);
|
||||
|
||||
CachedUser cached = cache.get(userId, CachedUser.class);
|
||||
if (cached == null) {
|
||||
cached = new CachedUser(loaded, realm, model);
|
||||
cache.addRevisioned(cached, startupRevision);
|
||||
}
|
||||
UserAdapter adapter = new UserAdapter(cached, this, session, realm);
|
||||
managedUsers.put(userId, adapter);
|
||||
return adapter;
|
||||
} else {
|
||||
userId = query.getUsers().iterator().next();
|
||||
if (invalidations.contains(userId)) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getUserByFederatedIdentity(socialLink, realm);
|
||||
|
||||
}
|
||||
return getUserById(userId, realm);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -346,12 +395,42 @@ public class UserCacheSession implements CacheUserProvider {
|
|||
|
||||
@Override
|
||||
public Set<FederatedIdentityModel> getFederatedIdentities(UserModel user, RealmModel realm) {
|
||||
return getDelegate().getFederatedIdentities(user, realm);
|
||||
logger.tracev("getFederatedIdentities: {0}", user.getUsername());
|
||||
|
||||
String cacheKey = getFederatedIdentityLinksCacheKey(user.getId());
|
||||
if (realmInvalidations.contains(realm.getId()) || invalidations.contains(user.getId()) || invalidations.contains(cacheKey)) {
|
||||
return getDelegate().getFederatedIdentities(user, realm);
|
||||
}
|
||||
|
||||
CachedFederatedIdentityLinks cachedLinks = cache.get(cacheKey, CachedFederatedIdentityLinks.class);
|
||||
|
||||
if (cachedLinks == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
Set<FederatedIdentityModel> federatedIdentities = getDelegate().getFederatedIdentities(user, realm);
|
||||
cachedLinks = new CachedFederatedIdentityLinks(loaded, cacheKey, realm, federatedIdentities);
|
||||
cache.addRevisioned(cachedLinks, startupRevision);
|
||||
return federatedIdentities;
|
||||
} else {
|
||||
return new HashSet<>(cachedLinks.getFederatedIdentities());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public FederatedIdentityModel getFederatedIdentity(UserModel user, String socialProvider, RealmModel realm) {
|
||||
return getDelegate().getFederatedIdentity(user, socialProvider, realm);
|
||||
logger.tracev("getFederatedIdentity: {0} {1}", user.getUsername(), socialProvider);
|
||||
|
||||
String cacheKey = getFederatedIdentityLinksCacheKey(user.getId());
|
||||
if (realmInvalidations.contains(realm.getId()) || invalidations.contains(user.getId()) || invalidations.contains(cacheKey)) {
|
||||
return getDelegate().getFederatedIdentity(user, socialProvider, realm);
|
||||
}
|
||||
|
||||
Set<FederatedIdentityModel> federatedIdentities = getFederatedIdentities(user, realm);
|
||||
for (FederatedIdentityModel socialLink : federatedIdentities) {
|
||||
if (socialLink.getIdentityProvider().equals(socialProvider)) {
|
||||
return socialLink;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -374,10 +453,22 @@ public class UserCacheSession implements CacheUserProvider {
|
|||
|
||||
protected void invalidateUser(RealmModel realm, UserModel user) {
|
||||
// just in case the transaction is rolled back you need to invalidate the user and all cache queries for that user
|
||||
|
||||
if (realm.isIdentityFederationEnabled()) {
|
||||
// Invalidate all keys for lookup this user by any identityProvider link
|
||||
Set<FederatedIdentityModel> federatedIdentities = getFederatedIdentities(user, realm);
|
||||
for (FederatedIdentityModel socialLink : federatedIdentities) {
|
||||
String fedIdentityCacheKey = getUserByFederatedIdentityCacheKey(realm.getId(), socialLink);
|
||||
invalidations.add(fedIdentityCacheKey);
|
||||
}
|
||||
|
||||
// Invalidate federationLinks of user
|
||||
invalidations.add(getFederatedIdentityLinksCacheKey(user.getId()));
|
||||
}
|
||||
|
||||
invalidations.add(user.getId());
|
||||
if (user.getEmail() != null) invalidations.add(getUserByEmailCacheKey(realm.getId(), user.getEmail()));
|
||||
invalidations.add(getUserByUsernameCacheKey(realm.getId(), user.getUsername()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -388,16 +479,25 @@ public class UserCacheSession implements CacheUserProvider {
|
|||
|
||||
@Override
|
||||
public void addFederatedIdentity(RealmModel realm, UserModel user, FederatedIdentityModel socialLink) {
|
||||
invalidations.add(getFederatedIdentityLinksCacheKey(user.getId()));
|
||||
getDelegate().addFederatedIdentity(realm, user, socialLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateFederatedIdentity(RealmModel realm, UserModel federatedUser, FederatedIdentityModel federatedIdentityModel) {
|
||||
invalidations.add(getFederatedIdentityLinksCacheKey(federatedUser.getId()));
|
||||
getDelegate().updateFederatedIdentity(realm, federatedUser, federatedIdentityModel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeFederatedIdentity(RealmModel realm, UserModel user, String socialProvider) {
|
||||
// Needs to invalidate both directions
|
||||
FederatedIdentityModel socialLink = getFederatedIdentity(user, socialProvider, realm);
|
||||
invalidations.add(getFederatedIdentityLinksCacheKey(user.getId()));
|
||||
if (socialLink != null) {
|
||||
invalidations.add(getUserByFederatedIdentityCacheKey(realm.getId(), socialLink));
|
||||
}
|
||||
|
||||
return getDelegate().removeFederatedIdentity(realm, user, socialProvider);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.models.cache.infinispan.entities;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.keycloak.models.FederatedIdentityModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
/**
|
||||
* The cache entry, which contains list of all identityProvider links for particular user. It needs to be updated every time when any
|
||||
* federation link is added, removed or updated for the user
|
||||
*
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
*/
|
||||
public class CachedFederatedIdentityLinks extends AbstractRevisioned implements InRealm {
|
||||
|
||||
private final String realmId;
|
||||
private final Set<FederatedIdentityModel> federatedIdentities = new HashSet<>();
|
||||
|
||||
public CachedFederatedIdentityLinks(Long revision, String id, RealmModel realm, Set<FederatedIdentityModel> federatedIdentities) {
|
||||
super(revision, id);
|
||||
this.realmId = realm.getId();
|
||||
this.federatedIdentities.addAll(federatedIdentities);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealm() {
|
||||
return realmId;
|
||||
}
|
||||
|
||||
public Set<FederatedIdentityModel> getFederatedIdentities() {
|
||||
return federatedIdentities;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue