commit
8bcb01fadb
12 changed files with 522 additions and 26 deletions
|
@ -16,4 +16,6 @@ public interface CacheKeycloakSession extends KeycloakSession {
|
||||||
void registerRoleInvalidation(String id);
|
void registerRoleInvalidation(String id);
|
||||||
|
|
||||||
void registerOAuthClientInvalidation(String id);
|
void registerOAuthClientInvalidation(String id);
|
||||||
|
|
||||||
|
void registerUserInvalidation(String id);
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,8 +174,9 @@ public abstract class ClientAdapter implements ClientModel {
|
||||||
|
|
||||||
public boolean hasScope(RoleModel role) {
|
public boolean hasScope(RoleModel role) {
|
||||||
if (updatedClient != null) return updatedClient.hasScope(role);
|
if (updatedClient != null) return updatedClient.hasScope(role);
|
||||||
|
if (cachedClient.getScope().contains(role.getId())) return true;
|
||||||
|
|
||||||
Set<RoleModel> roles = getScopeMappings();
|
Set<RoleModel> roles = getScopeMappings();
|
||||||
if (roles.contains(role)) return true;
|
|
||||||
|
|
||||||
for (RoleModel mapping : roles) {
|
for (RoleModel mapping : roles) {
|
||||||
if (mapping.hasRole(role)) return true;
|
if (mapping.hasRole(role)) return true;
|
||||||
|
|
|
@ -17,6 +17,7 @@ import org.keycloak.models.cache.entities.CachedOAuthClient;
|
||||||
import org.keycloak.models.cache.entities.CachedRealm;
|
import org.keycloak.models.cache.entities.CachedRealm;
|
||||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||||
import org.keycloak.models.cache.entities.CachedRole;
|
import org.keycloak.models.cache.entities.CachedRole;
|
||||||
|
import org.keycloak.models.cache.entities.CachedUser;
|
||||||
import org.keycloak.provider.ProviderSession;
|
import org.keycloak.provider.ProviderSession;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -41,10 +42,12 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession {
|
||||||
protected Set<String> appInvalidations = new HashSet<String>();
|
protected Set<String> appInvalidations = new HashSet<String>();
|
||||||
protected Set<String> roleInvalidations = new HashSet<String>();
|
protected Set<String> roleInvalidations = new HashSet<String>();
|
||||||
protected Set<String> clientInvalidations = 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, RealmModel> managedRealms = new HashMap<String, RealmModel>();
|
||||||
protected Map<String, ApplicationModel> managedApplications = new HashMap<String, ApplicationModel>();
|
protected Map<String, ApplicationModel> managedApplications = new HashMap<String, ApplicationModel>();
|
||||||
protected Map<String, OAuthClientModel> managedClients = new HashMap<String, OAuthClientModel>();
|
protected Map<String, OAuthClientModel> managedClients = new HashMap<String, OAuthClientModel>();
|
||||||
protected Map<String, RoleModel> managedRoles = new HashMap<String, RoleModel>();
|
protected Map<String, RoleModel> managedRoles = new HashMap<String, RoleModel>();
|
||||||
|
protected Map<String, UserModel> managedUsers = new HashMap<String, UserModel>();
|
||||||
|
|
||||||
protected boolean clearAll;
|
protected boolean clearAll;
|
||||||
|
|
||||||
|
@ -88,6 +91,11 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession {
|
||||||
clientInvalidations.add(id);
|
clientInvalidations.add(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerUserInvalidation(String id) {
|
||||||
|
userInvalidations.add(id);
|
||||||
|
}
|
||||||
|
|
||||||
protected void runInvalidations() {
|
protected void runInvalidations() {
|
||||||
for (String id : realmInvalidations) {
|
for (String id : realmInvalidations) {
|
||||||
cache.invalidateCachedRealmById(id);
|
cache.invalidateCachedRealmById(id);
|
||||||
|
@ -101,6 +109,9 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession {
|
||||||
for (String id : clientInvalidations) {
|
for (String id : clientInvalidations) {
|
||||||
cache.invalidateCachedOAuthClientById(id);
|
cache.invalidateCachedOAuthClientById(id);
|
||||||
}
|
}
|
||||||
|
for (String id : userInvalidations) {
|
||||||
|
cache.invalidateCachedUserById(id);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,17 +221,59 @@ public class DefaultCacheKeycloakSession implements CacheKeycloakSession {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getUserById(String id, RealmModel realm) {
|
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
|
@Override
|
||||||
public UserModel getUserByUsername(String username, RealmModel realm) {
|
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
|
@Override
|
||||||
public UserModel getUserByEmail(String email, RealmModel realm) {
|
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
|
@Override
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package org.keycloak.models.cache;
|
package org.keycloak.models.cache;
|
||||||
|
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.cache.entities.CachedApplication;
|
import org.keycloak.models.cache.entities.CachedApplication;
|
||||||
import org.keycloak.models.cache.entities.CachedOAuthClient;
|
import org.keycloak.models.cache.entities.CachedOAuthClient;
|
||||||
import org.keycloak.models.cache.entities.CachedRealm;
|
import org.keycloak.models.cache.entities.CachedRealm;
|
||||||
import org.keycloak.models.cache.entities.CachedRole;
|
import org.keycloak.models.cache.entities.CachedRole;
|
||||||
|
import org.keycloak.models.cache.entities.CachedUser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -48,4 +50,17 @@ public interface KeycloakCache {
|
||||||
|
|
||||||
|
|
||||||
void invalidateRoleById(String id);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -278,4 +278,9 @@ public class NoCacheKeycloakSession implements CacheKeycloakSession {
|
||||||
public void removeUserSessions(RealmModel realm) {
|
public void removeUserSessions(RealmModel realm) {
|
||||||
getDelegate().removeUserSessions(realm);
|
getDelegate().removeUserSessions(realm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerUserInvalidation(String id) {
|
||||||
|
//To change body of implemented methods use File | Settings | File Templates.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,18 +1,15 @@
|
||||||
package org.keycloak.models.cache;
|
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.RealmModel;
|
||||||
import org.keycloak.models.cache.entities.CachedApplication;
|
import org.keycloak.models.cache.entities.CachedApplication;
|
||||||
import org.keycloak.models.cache.entities.CachedOAuthClient;
|
import org.keycloak.models.cache.entities.CachedOAuthClient;
|
||||||
import org.keycloak.models.cache.entities.CachedRealm;
|
import org.keycloak.models.cache.entities.CachedRealm;
|
||||||
import org.keycloak.models.cache.entities.CachedRole;
|
import org.keycloak.models.cache.entities.CachedRole;
|
||||||
import org.keycloak.provider.ProviderSession;
|
import org.keycloak.models.cache.entities.CachedUser;
|
||||||
import org.keycloak.provider.ProviderSessionFactory;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +24,121 @@ public class SimpleCache implements KeycloakCache {
|
||||||
protected ConcurrentHashMap<String, CachedOAuthClient> clientCache = new ConcurrentHashMap<String, CachedOAuthClient>();
|
protected ConcurrentHashMap<String, CachedOAuthClient> clientCache = new ConcurrentHashMap<String, CachedOAuthClient>();
|
||||||
protected ConcurrentHashMap<String, CachedRole> roleCache = new ConcurrentHashMap<String, CachedRole>();
|
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
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
realmCache.clear();
|
realmCache.clear();
|
||||||
|
@ -34,6 +146,7 @@ public class SimpleCache implements KeycloakCache {
|
||||||
applicationCache.clear();
|
applicationCache.clear();
|
||||||
clientCache.clear();
|
clientCache.clear();
|
||||||
roleCache.clear();
|
roleCache.clear();
|
||||||
|
usersById.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
283
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
vendored
Executable file
283
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
vendored
Executable file
|
@ -0,0 +1,283 @@
|
||||||
|
package org.keycloak.models.cache;
|
||||||
|
|
||||||
|
import org.keycloak.models.ApplicationModel;
|
||||||
|
import org.keycloak.models.AuthenticationLinkModel;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleContainerModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.cache.entities.CachedUser;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class UserAdapter implements UserModel {
|
||||||
|
protected UserModel updated;
|
||||||
|
protected CachedUser cached;
|
||||||
|
protected KeycloakCache cache;
|
||||||
|
protected CacheKeycloakSession cacheSession;
|
||||||
|
protected RealmModel realm;
|
||||||
|
|
||||||
|
public UserAdapter(CachedUser cached, KeycloakCache cache, CacheKeycloakSession session, RealmModel realm) {
|
||||||
|
this.cached = cached;
|
||||||
|
this.cache = cache;
|
||||||
|
this.cacheSession = session;
|
||||||
|
this.realm = realm;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void getDelegateForUpdate() {
|
||||||
|
if (updated == null) {
|
||||||
|
cacheSession.registerUserInvalidation(getId());
|
||||||
|
updated = cacheSession.getDelegate().getUserById(getId(), realm);
|
||||||
|
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
if (updated != null) return updated.getId();
|
||||||
|
return cached.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLoginName() {
|
||||||
|
if (updated != null) return updated.getLoginName();
|
||||||
|
return cached.getLoginName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLoginName(String loginName) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setLoginName(loginName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled() {
|
||||||
|
if (updated != null) return updated.isEnabled();
|
||||||
|
return cached.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTotp() {
|
||||||
|
if (updated != null) return updated.isTotp();
|
||||||
|
return cached.isTotp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabled(boolean enabled) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, String value) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAttribute(String name) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.removeAttribute(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getAttribute(String name) {
|
||||||
|
if (updated != null) return updated.getAttribute(name);
|
||||||
|
return cached.getAttributes().get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, String> getAttributes() {
|
||||||
|
if (updated != null) return updated.getAttributes();
|
||||||
|
return cached.getAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RequiredAction> getRequiredActions() {
|
||||||
|
if (updated != null) return updated.getRequiredActions();
|
||||||
|
return cached.getRequiredActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRequiredAction(RequiredAction action) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.addRequiredAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRequiredAction(RequiredAction action) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.removeRequiredAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstName() {
|
||||||
|
if (updated != null) return updated.getFirstName();
|
||||||
|
return cached.getFirstName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setFirstName(firstName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLastName() {
|
||||||
|
if (updated != null) return updated.getLastName();
|
||||||
|
return cached.getLastName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setLastName(lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEmail() {
|
||||||
|
if (updated != null) return updated.getEmail();
|
||||||
|
return cached.getEmail();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmail(String email) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmailVerified() {
|
||||||
|
if (updated != null) return updated.isEmailVerified();
|
||||||
|
return cached.isEmailVerified();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmailVerified(boolean verified) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setEmailVerified(verified);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTotp(boolean totp) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setTotp(totp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNotBefore() {
|
||||||
|
if (updated != null) return updated.getNotBefore();
|
||||||
|
return cached.getNotBefore();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setNotBefore(int notBefore) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setNotBefore(notBefore);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateCredential(UserCredentialModel cred) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.updateCredential(cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
||||||
|
if (updated != null) return updated.getCredentialsDirectly();
|
||||||
|
return cached.getCredentials();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateCredentialDirectly(UserCredentialValueModel cred) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.updateCredentialDirectly(cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuthenticationLinkModel getAuthenticationLink() {
|
||||||
|
if (updated != null) return updated.getAuthenticationLink();
|
||||||
|
return cached.getAuthenticationLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAuthenticationLink(AuthenticationLinkModel authenticationLink) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.setAuthenticationLink(authenticationLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRealmRoleMappings() {
|
||||||
|
if (updated != null) return updated.getRealmRoleMappings();
|
||||||
|
Set<RoleModel> roleMappings = getRoleMappings();
|
||||||
|
Set<RoleModel> realmMappings = new HashSet<RoleModel>();
|
||||||
|
for (RoleModel role : roleMappings) {
|
||||||
|
RoleContainerModel container = role.getContainer();
|
||||||
|
if (container instanceof RealmModel) {
|
||||||
|
if (((RealmModel) container).getId().equals(realm.getId())) {
|
||||||
|
realmMappings.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return realmMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getApplicationRoleMappings(ApplicationModel app) {
|
||||||
|
if (updated != null) return updated.getApplicationRoleMappings(app);
|
||||||
|
Set<RoleModel> roleMappings = getRoleMappings();
|
||||||
|
Set<RoleModel> appMappings = new HashSet<RoleModel>();
|
||||||
|
for (RoleModel role : roleMappings) {
|
||||||
|
RoleContainerModel container = role.getContainer();
|
||||||
|
if (container instanceof ApplicationModel) {
|
||||||
|
if (((ApplicationModel) container).getId().equals(app.getId())) {
|
||||||
|
appMappings.add(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return appMappings;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasRole(RoleModel role) {
|
||||||
|
if (updated != null) return updated.hasRole(role);
|
||||||
|
if (cached.getRoleMappings().contains(role.getId())) return true;
|
||||||
|
|
||||||
|
Set<RoleModel> mappings = getRoleMappings();
|
||||||
|
for (RoleModel mapping: mappings) {
|
||||||
|
if (mapping.hasRole(role)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void grantRole(RoleModel role) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.grantRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<RoleModel> getRoleMappings() {
|
||||||
|
if (updated != null) return updated.getRoleMappings();
|
||||||
|
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||||
|
for (String id : cached.getRoleMappings()) {
|
||||||
|
roles.add(cacheSession.getRoleById(id, realm));
|
||||||
|
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteRoleMapping(RoleModel role) {
|
||||||
|
getDelegateForUpdate();
|
||||||
|
updated.deleteRoleMapping(role);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package org.keycloak.models.cache.entities;
|
package org.keycloak.models.cache.entities;
|
||||||
|
|
||||||
|
import org.keycloak.models.AuthenticationLinkModel;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -18,32 +20,40 @@ import java.util.Set;
|
||||||
public class CachedUser {
|
public class CachedUser {
|
||||||
private String id;
|
private String id;
|
||||||
private String loginName;
|
private String loginName;
|
||||||
|
private String usernameKey;
|
||||||
private String firstName;
|
private String firstName;
|
||||||
private String lastName;
|
private String lastName;
|
||||||
private String email;
|
private String email;
|
||||||
|
private String emailKey;
|
||||||
private boolean emailVerified;
|
private boolean emailVerified;
|
||||||
private int notBefore;
|
private int notBefore;
|
||||||
private List<UserCredentialValueModel> credentials = new LinkedList<UserCredentialValueModel>();
|
private List<UserCredentialValueModel> credentials = new LinkedList<UserCredentialValueModel>();
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
private boolean totp;
|
private boolean totp;
|
||||||
|
private AuthenticationLinkModel authenticationLink;
|
||||||
private Map<String, String> attributes = new HashMap<String, String>();
|
private Map<String, String> attributes = new HashMap<String, String>();
|
||||||
private Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
|
private Set<UserModel.RequiredAction> requiredActions = new HashSet<UserModel.RequiredAction>();
|
||||||
private Set<String> roleMappings = new HashSet<String>();
|
private Set<String> roleMappings = new HashSet<String>();
|
||||||
|
|
||||||
|
|
||||||
public CachedUser(UserModel user) {
|
public CachedUser(RealmModel realm, UserModel user) {
|
||||||
this.id = user.getId();
|
this.id = user.getId();
|
||||||
this.loginName = user.getLoginName();
|
this.loginName = user.getLoginName();
|
||||||
|
this.usernameKey = realm.getId() + "." + this.loginName;
|
||||||
this.firstName = user.getFirstName();
|
this.firstName = user.getFirstName();
|
||||||
this.lastName = user.getLastName();
|
this.lastName = user.getLastName();
|
||||||
this.attributes.putAll(user.getAttributes());
|
this.attributes.putAll(user.getAttributes());
|
||||||
this.email = user.getEmail();
|
this.email = user.getEmail();
|
||||||
|
if (this.email != null) {
|
||||||
|
this.emailKey = realm.getId() + "." + this.email;
|
||||||
|
}
|
||||||
this.emailVerified = user.isEmailVerified();
|
this.emailVerified = user.isEmailVerified();
|
||||||
this.notBefore = user.getNotBefore();
|
this.notBefore = user.getNotBefore();
|
||||||
this.credentials.addAll(user.getCredentialsDirectly());
|
this.credentials.addAll(user.getCredentialsDirectly());
|
||||||
this.enabled = user.isEnabled();
|
this.enabled = user.isEnabled();
|
||||||
this.totp = user.isTotp();
|
this.totp = user.isTotp();
|
||||||
this.requiredActions.addAll(user.getRequiredActions());
|
this.requiredActions.addAll(user.getRequiredActions());
|
||||||
|
this.authenticationLink = user.getAuthenticationLink();
|
||||||
for (RoleModel role : user.getRoleMappings()) {
|
for (RoleModel role : user.getRoleMappings()) {
|
||||||
roleMappings.add(role.getId());
|
roleMappings.add(role.getId());
|
||||||
}
|
}
|
||||||
|
@ -57,6 +67,14 @@ public class CachedUser {
|
||||||
return loginName;
|
return loginName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getUsernameKey() {
|
||||||
|
return usernameKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmailKey() {
|
||||||
|
return emailKey;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
@ -100,4 +118,8 @@ public class CachedUser {
|
||||||
public Set<String> getRoleMappings() {
|
public Set<String> getRoleMappings() {
|
||||||
return roleMappings;
|
return roleMappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthenticationLinkModel getAuthenticationLink() {
|
||||||
|
return authenticationLink;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -254,7 +254,8 @@ public class MongoKeycloakSession implements KeycloakSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<SocialLinkModel> getSocialLinks(UserModel user, RealmModel realm) {
|
public Set<SocialLinkModel> getSocialLinks(UserModel userModel, RealmModel realm) {
|
||||||
|
UserModel user = getUserById(userModel.getId(), realm);
|
||||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||||
List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
|
List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
|
||||||
|
|
||||||
|
@ -271,7 +272,8 @@ public class MongoKeycloakSession implements KeycloakSession {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
|
private SocialLinkEntity findSocialLink(UserModel userModel, String socialProvider, RealmModel realm) {
|
||||||
|
UserModel user = getUserById(userModel.getId(), realm);
|
||||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||||
List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
|
List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
|
||||||
if (linkEntities == null) {
|
if (linkEntities == null) {
|
||||||
|
@ -289,7 +291,7 @@ public class MongoKeycloakSession implements KeycloakSession {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
|
public SocialLinkModel getSocialLink(UserModel user, String socialProvider, RealmModel realm) {
|
||||||
SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider);
|
SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider, realm);
|
||||||
return socialLinkEntity != null ? new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername()) : null;
|
return socialLinkEntity != null ? new SocialLinkModel(socialLinkEntity.getSocialProvider(), socialLinkEntity.getSocialUserId(), socialLinkEntity.getSocialUsername()) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -857,18 +857,17 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean removeSocialLink(UserModel user, String socialProvider) {
|
public boolean removeSocialLink(UserModel userModel, String socialProvider) {
|
||||||
SocialLinkEntity socialLinkEntity = findSocialLink(user, socialProvider);
|
UserModel user = getUserById(userModel.getId());
|
||||||
|
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
||||||
|
SocialLinkEntity socialLinkEntity = findSocialLink(userEntity, socialProvider);
|
||||||
if (socialLinkEntity == null) {
|
if (socialLinkEntity == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
|
||||||
|
|
||||||
return getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
|
return getMongoStore().pullItemFromList(userEntity, "socialLinks", socialLinkEntity, invocationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SocialLinkEntity findSocialLink(UserModel user, String socialProvider) {
|
private SocialLinkEntity findSocialLink(MongoUserEntity userEntity, String socialProvider) {
|
||||||
MongoUserEntity userEntity = ((UserAdapter) user).getUser();
|
|
||||||
List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
|
List<SocialLinkEntity> linkEntities = userEntity.getSocialLinks();
|
||||||
if (linkEntities == null) {
|
if (linkEntities == null) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
"modelCache": {
|
"modelCache": {
|
||||||
"provider": "${keycloak.model.cache.provider:none}"
|
"provider": "${keycloak.model.cache.provider:simple}"
|
||||||
},
|
},
|
||||||
|
|
||||||
"timer": {
|
"timer": {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
import org.keycloak.models.PasswordPolicy;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.TimeBasedOTP;
|
import org.keycloak.models.utils.TimeBasedOTP;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
|
@ -144,7 +145,7 @@ public class AccountTest {
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void after() {
|
public void after() {
|
||||||
keycloakRule.configure(new KeycloakSetup() {
|
keycloakRule.update(new KeycloakSetup() {
|
||||||
@Override
|
@Override
|
||||||
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
|
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
|
||||||
UserModel user = appRealm.getUser("test-user@localhost");
|
UserModel user = appRealm.getUser("test-user@localhost");
|
||||||
|
@ -239,7 +240,7 @@ public class AccountTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void changePasswordWithPasswordPolicy() {
|
public void changePasswordWithPasswordPolicy() {
|
||||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||||
@Override
|
@Override
|
||||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
appRealm.setPasswordPolicy(new PasswordPolicy("length"));
|
appRealm.setPasswordPolicy(new PasswordPolicy("length"));
|
||||||
|
@ -263,11 +264,11 @@ public class AccountTest {
|
||||||
|
|
||||||
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
|
events.expectAccount(EventType.UPDATE_PASSWORD).assertEvent();
|
||||||
} finally {
|
} finally {
|
||||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||||
@Override
|
@Override
|
||||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||||
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue