Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ public interface CacheKeycloakSession extends KeycloakSession {
void registerRoleInvalidation(String id);

void registerOAuthClientInvalidation(String id);

void registerUserInvalidation(String id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,9 @@ public Set<RoleModel> getRealmScopeMappings() {

public boolean hasScope(RoleModel role) {
if (updatedClient != null) return updatedClient.hasScope(role);
if (cachedClient.getScope().contains(role.getId())) return true;

Set<RoleModel> roles = getScopeMappings();
if (roles.contains(role)) return true;

for (RoleModel mapping : roles) {
if (mapping.hasRole(role)) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.keycloak.models.cache.entities.CachedRealm;
import org.keycloak.models.cache.entities.CachedRealmRole;
import org.keycloak.models.cache.entities.CachedRole;
import org.keycloak.models.cache.entities.CachedUser;
import org.keycloak.provider.ProviderSession;

import java.util.HashMap;
Expand All @@ -41,10 +42,12 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession {
protected Set<String> appInvalidations = new HashSet<String>();
protected Set<String> roleInvalidations = new HashSet<String>();
protected Set<String> clientInvalidations = new HashSet<String>();
protected Set<String> userInvalidations = new HashSet<String>();
protected Map<String, RealmModel> managedRealms = new HashMap<String, RealmModel>();
protected Map<String, ApplicationModel> managedApplications = new HashMap<String, ApplicationModel>();
protected Map<String, OAuthClientModel> managedClients = new HashMap<String, OAuthClientModel>();
protected Map<String, RoleModel> managedRoles = new HashMap<String, RoleModel>();
protected Map<String, UserModel> managedUsers = new HashMap<String, UserModel>();

protected boolean clearAll;

Expand Down Expand Up @@ -88,6 +91,11 @@ public void registerOAuthClientInvalidation(String id) {
clientInvalidations.add(id);
}

@Override
public void registerUserInvalidation(String id) {
userInvalidations.add(id);
}

protected void runInvalidations() {
for (String id : realmInvalidations) {
cache.invalidateCachedRealmById(id);
Expand All @@ -101,6 +109,9 @@ protected void runInvalidations() {
for (String id : clientInvalidations) {
cache.invalidateCachedOAuthClientById(id);
}
for (String id : userInvalidations) {
cache.invalidateCachedUserById(id);
}

}

Expand Down Expand Up @@ -210,17 +221,59 @@ public RealmModel getRealmByName(String name) {

@Override
public UserModel getUserById(String id, RealmModel realm) {
return getDelegate().getUserById(id, realm);
CachedUser cached = cache.getCachedUser(id);
if (cached == null) {
UserModel model = getDelegate().getUserById(id, realm);
if (model == null) return null;
if (userInvalidations.contains(id)) return model;
cached = new CachedUser(realm, model);
cache.addCachedUser(cached);
} else if (userInvalidations.contains(id)) {
return getDelegate().getUserById(id, realm);
} else if (managedUsers.containsKey(id)) {
return managedUsers.get(id);
}
UserAdapter adapter = new UserAdapter(cached, cache, this, realm);
managedUsers.put(id, adapter);
return adapter;
}

@Override
public UserModel getUserByUsername(String username, RealmModel realm) {
return getDelegate().getUserByUsername(username, realm);
CachedUser cached = cache.getCachedUserByUsername(username, realm);
if (cached == null) {
UserModel model = getDelegate().getUserByUsername(username, realm);
if (model == null) return null;
if (userInvalidations.contains(model.getId())) return model;
cached = new CachedUser(realm, model);
cache.addCachedUser(cached);
} else if (userInvalidations.contains(cached.getId())) {
return getDelegate().getUserById(cached.getId(), realm);
} else if (managedUsers.containsKey(cached.getId())) {
return managedUsers.get(cached.getId());
}
UserAdapter adapter = new UserAdapter(cached, cache, this, realm);
managedUsers.put(cached.getId(), adapter);
return adapter;
}

@Override
public UserModel getUserByEmail(String email, RealmModel realm) {
return getDelegate().getUserByEmail(email, realm);
CachedUser cached = cache.getCachedUserByEmail(email, realm);
if (cached == null) {
UserModel model = getDelegate().getUserByEmail(email, realm);
if (model == null) return null;
if (userInvalidations.contains(model.getId())) return model;
cached = new CachedUser(realm, model);
cache.addCachedUser(cached);
} else if (userInvalidations.contains(cached.getId())) {
return getDelegate().getUserByEmail(email, realm);
} else if (managedUsers.containsKey(cached.getId())) {
return managedUsers.get(cached.getId());
}
UserAdapter adapter = new UserAdapter(cached, cache, this, realm);
managedUsers.put(cached.getId(), adapter);
return adapter;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package org.keycloak.models.cache;

import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.entities.CachedApplication;
import org.keycloak.models.cache.entities.CachedOAuthClient;
import org.keycloak.models.cache.entities.CachedRealm;
import org.keycloak.models.cache.entities.CachedRole;
import org.keycloak.models.cache.entities.CachedUser;

/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
Expand Down Expand Up @@ -48,4 +50,17 @@ public interface KeycloakCache {


void invalidateRoleById(String id);

CachedUser getCachedUser(String id);

void invalidateCachedUser(CachedUser user);

void addCachedUser(CachedUser user);

CachedUser getCachedUserByUsername(String name, RealmModel realm);
CachedUser getCachedUserByEmail(String name, RealmModel realm);

void invalidedCachedUserById(String id);

void invalidateCachedUserById(String id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -278,4 +278,9 @@ public void removeExpiredUserSessions(RealmModel realm) {
public void removeUserSessions(RealmModel realm) {
getDelegate().removeUserSessions(realm);
}

@Override
public void registerUserInvalidation(String id) {
//To change body of implemented methods use File | Settings | File Templates.
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
package org.keycloak.models.cache;

import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.RealmModel;
import org.keycloak.models.cache.entities.CachedApplication;
import org.keycloak.models.cache.entities.CachedOAuthClient;
import org.keycloak.models.cache.entities.CachedRealm;
import org.keycloak.models.cache.entities.CachedRole;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.models.cache.entities.CachedUser;

import java.util.List;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
Expand All @@ -27,13 +24,129 @@ public class SimpleCache implements KeycloakCache {
protected ConcurrentHashMap<String, CachedOAuthClient> clientCache = new ConcurrentHashMap<String, CachedOAuthClient>();
protected ConcurrentHashMap<String, CachedRole> roleCache = new ConcurrentHashMap<String, CachedRole>();

protected int maxUserCacheSize = 10000;
protected boolean userCacheEnabled = true;

protected Map<String, CachedUser> usersById = Collections.synchronizedMap(new LRUCache());
protected Map<String, CachedUser> usersByUsername = new ConcurrentHashMap<String, CachedUser>();
protected Map<String, CachedUser> usersByEmail = new ConcurrentHashMap<String, CachedUser>();

protected class LRUCache extends LinkedHashMap<String, CachedUser> {
public LRUCache() {
super(1000, 1.1F, true);
}

@Override
public CachedUser put(String key, CachedUser value) {
usersByUsername.put(value.getUsernameKey(), value);
if (value.getEmail() != null) {
usersByEmail.put(value.getEmailKey(), value);
}
return super.put(key, value);
}

@Override
public CachedUser remove(Object key) {
CachedUser user = super.remove(key);
if (user == null) return null;
removeUser(user);
return user;
}

@Override
public void clear() {
super.clear();
usersByUsername.clear();
usersByEmail.clear();
}

@Override
protected boolean removeEldestEntry(Map.Entry<String, CachedUser> eldest) {
boolean evict = size() > maxUserCacheSize;
if (evict) {
removeUser(eldest.getValue());
}
return evict;
}

private void removeUser(CachedUser value) {
usersByUsername.remove(value.getUsernameKey());
if (value.getEmail() != null) usersByEmail.remove(value.getEmailKey());
}
}

public int getMaxUserCacheSize() {
return maxUserCacheSize;
}

public void setMaxUserCacheSize(int maxUserCacheSize) {
this.maxUserCacheSize = maxUserCacheSize;
}

public boolean isUserCacheEnabled() {
return userCacheEnabled;
}

public void setUserCacheEnabled(boolean userCacheEnabled) {
this.userCacheEnabled = userCacheEnabled;
}

@Override
public CachedUser getCachedUser(String id) {
if (!userCacheEnabled) return null;
return usersById.get(id);
}

@Override
public void invalidateCachedUser(CachedUser user) {
if (!userCacheEnabled) return;
usersById.remove(user.getId());
}

@Override
public void invalidateCachedUserById(String id) {
if (!userCacheEnabled) return;
usersById.remove(id);
}

@Override
public void addCachedUser(CachedUser user) {
if (!userCacheEnabled) return;
usersById.put(user.getId(), user);
}

@Override
public CachedUser getCachedUserByUsername(String name, RealmModel realm) {
if (!userCacheEnabled) return null;
CachedUser user = usersByUsername.get(realm.getId() + "." +name);
if (user == null) return null;
usersById.get(user.getId()); // refresh cache entry age
return user;
}

@Override
public CachedUser getCachedUserByEmail(String name, RealmModel realm) {
if (!userCacheEnabled) return null;
CachedUser user = usersByEmail.get(realm.getId() + "." +name);
if (user == null) return null;
usersById.get(user.getId()); // refresh cache entry age
return user;
}

@Override
public void invalidedCachedUserById(String id) {
if (!userCacheEnabled) return;
usersById.remove(id);
}

@Override
public void clear() {
realmCache.clear();
realmCacheByName.clear();
applicationCache.clear();
clientCache.clear();
roleCache.clear();
usersById.clear();
}

@Override
Expand Down
Loading