commit
c45e40db99
82 changed files with 2466 additions and 2501 deletions
|
@ -23,7 +23,7 @@
|
|||
},
|
||||
|
||||
"userCache": {
|
||||
"infinispan" : {
|
||||
"default" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
@ -61,15 +61,15 @@
|
|||
},
|
||||
|
||||
"realmCache": {
|
||||
"provider": "infinispan-locking",
|
||||
"infinispan-locking" : {
|
||||
"provider": "default",
|
||||
"default" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
||||
"connectionsInfinispan": {
|
||||
"provider": "locking",
|
||||
"locking": {
|
||||
"provider": "default",
|
||||
"default": {
|
||||
"cacheContainer" : "java:comp/env/infinispan/Keycloak"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.infinispan.configuration.global.GlobalConfigurationBuilder;
|
|||
import org.infinispan.manager.DefaultCacheManager;
|
||||
import org.infinispan.manager.EmbeddedCacheManager;
|
||||
import org.infinispan.transaction.LockingMode;
|
||||
import org.infinispan.transaction.TransactionMode;
|
||||
import org.infinispan.transaction.lookup.DummyTransactionManagerLookup;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
|
@ -154,6 +155,15 @@ public class DefaultInfinispanConnectionProviderFactory implements InfinispanCon
|
|||
}
|
||||
Configuration replicationCacheConfiguration = replicationConfigBuilder.build();
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.WORK_CACHE_NAME, replicationCacheConfiguration);
|
||||
|
||||
ConfigurationBuilder counterConfigBuilder = new ConfigurationBuilder();
|
||||
counterConfigBuilder.invocationBatching().enable()
|
||||
.transaction().transactionMode(TransactionMode.TRANSACTIONAL);
|
||||
counterConfigBuilder.transaction().transactionManagerLookup(new DummyTransactionManagerLookup());
|
||||
counterConfigBuilder.transaction().lockingMode(LockingMode.PESSIMISTIC);
|
||||
Configuration counterCacheConfiguration = counterConfigBuilder.build();
|
||||
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.VERSION_CACHE_NAME, counterCacheConfiguration);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
1
model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java
Normal file → Executable file
1
model/infinispan/src/main/java/org/keycloak/connections/infinispan/InfinispanConnectionProvider.java
Normal file → Executable file
|
@ -25,6 +25,7 @@ import org.keycloak.provider.Provider;
|
|||
*/
|
||||
public interface InfinispanConnectionProvider extends Provider {
|
||||
|
||||
public static final String VERSION_CACHE_NAME = "realmVersions";
|
||||
static final String REALM_CACHE_NAME = "realms";
|
||||
static final String USER_CACHE_NAME = "users";
|
||||
static final String SESSION_CACHE_NAME = "sessions";
|
||||
|
|
|
@ -19,8 +19,7 @@ package org.keycloak.models.cache.infinispan;
|
|||
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClient;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
@ -45,7 +44,7 @@ public class ClientAdapter implements ClientModel {
|
|||
|
||||
private void getDelegateForUpdate() {
|
||||
if (updated == null) {
|
||||
cacheSession.registerApplicationInvalidation(getId());
|
||||
cacheSession.registerClientInvalidation(getId());
|
||||
updated = cacheSession.getDelegate().getClientById(getId(), cachedRealm);
|
||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
||||
}
|
||||
|
@ -377,7 +376,6 @@ public class ClientAdapter implements ClientModel {
|
|||
public void setClientId(String clientId) {
|
||||
getDelegateForUpdate();
|
||||
updated.setClientId(clientId);
|
||||
cacheSession.registerRealmInvalidation(cachedRealm.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -465,11 +463,18 @@ public class ClientAdapter implements ClientModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateDefaultRoles(String[] defaultRoles) {
|
||||
public void updateDefaultRoles(String... defaultRoles) {
|
||||
getDelegateForUpdate();
|
||||
updated.updateDefaultRoles(defaultRoles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDefaultRoles(String... defaultRoles) {
|
||||
getDelegateForUpdate();
|
||||
updated.removeDefaultRoles(defaultRoles);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBearerOnly() {
|
||||
if (updated != null) return updated.isBearerOnly();
|
||||
|
@ -544,12 +549,10 @@ public class ClientAdapter implements ClientModel {
|
|||
|
||||
@Override
|
||||
public RoleModel getRole(String name) {
|
||||
if (updated != null) return updated.getRole(name);
|
||||
String id = cached.getRoles().get(name);
|
||||
if (id == null) {
|
||||
return null;
|
||||
for (RoleModel role : getRoles()) {
|
||||
if (role.getName().equals(name)) return role;
|
||||
}
|
||||
return cacheSession.getRoleById(id, cachedRealm);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -577,15 +580,7 @@ public class ClientAdapter implements ClientModel {
|
|||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles() {
|
||||
if (updated != null) return updated.getRoles();
|
||||
|
||||
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||
for (String id : cached.getRoles().values()) {
|
||||
RoleModel roleById = cacheSession.getRoleById(id, cachedRealm);
|
||||
if (roleById == null) continue;
|
||||
roles.add(roleById);
|
||||
}
|
||||
return roles;
|
||||
return cacheSession.getClientRoles(cachedRealm, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -24,8 +24,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
@ -39,14 +38,12 @@ import java.util.Set;
|
|||
public class ClientTemplateAdapter implements ClientTemplateModel {
|
||||
protected CacheRealmProvider cacheSession;
|
||||
protected RealmModel cachedRealm;
|
||||
protected RealmCache cache;
|
||||
|
||||
protected ClientTemplateModel updated;
|
||||
protected CachedClientTemplate cached;
|
||||
|
||||
public ClientTemplateAdapter(RealmModel cachedRealm, CachedClientTemplate cached, CacheRealmProvider cacheSession, RealmCache cache) {
|
||||
public ClientTemplateAdapter(RealmModel cachedRealm, CachedClientTemplate cached, CacheRealmProvider cacheSession) {
|
||||
this.cachedRealm = cachedRealm;
|
||||
this.cache = cache;
|
||||
this.cacheSession = cacheSession;
|
||||
this.cached = cached;
|
||||
}
|
||||
|
|
|
@ -1,396 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
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 DefaultCacheRealmProvider implements CacheRealmProvider {
|
||||
protected RealmCache cache;
|
||||
protected KeycloakSession session;
|
||||
protected RealmProvider delegate;
|
||||
protected boolean transactionActive;
|
||||
protected boolean setRollbackOnly;
|
||||
|
||||
protected Set<String> realmInvalidations = new HashSet<>();
|
||||
protected Set<String> appInvalidations = new HashSet<>();
|
||||
protected Set<String> clientTemplateInvalidations = new HashSet<>();
|
||||
protected Set<String> roleInvalidations = new HashSet<>();
|
||||
protected Set<String> groupInvalidations = new HashSet<>();
|
||||
protected Map<String, RealmModel> managedRealms = new HashMap<>();
|
||||
protected Map<String, ClientModel> managedApplications = new HashMap<>();
|
||||
protected Map<String, ClientTemplateModel> managedClientTemplates = new HashMap<>();
|
||||
protected Map<String, RoleModel> managedRoles = new HashMap<>();
|
||||
protected Map<String, GroupModel> managedGroups = new HashMap<>();
|
||||
|
||||
protected boolean clearAll;
|
||||
|
||||
public DefaultCacheRealmProvider(RealmCache cache, KeycloakSession session) {
|
||||
this.cache = cache;
|
||||
this.session = session;
|
||||
|
||||
session.getTransaction().enlistAfterCompletion(getTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return getDelegate().getMigrationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmProvider getDelegate() {
|
||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (delegate != null) return delegate;
|
||||
delegate = session.getProvider(RealmProvider.class);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRealmInvalidation(String id) {
|
||||
realmInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerApplicationInvalidation(String id) {
|
||||
appInvalidations.add(id);
|
||||
}
|
||||
@Override
|
||||
public void registerClientTemplateInvalidation(String id) {
|
||||
clientTemplateInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRoleInvalidation(String id) {
|
||||
roleInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerGroupInvalidation(String id) {
|
||||
groupInvalidations.add(id);
|
||||
|
||||
}
|
||||
|
||||
protected void runInvalidations() {
|
||||
for (String id : realmInvalidations) {
|
||||
cache.invalidateRealmById(id);
|
||||
}
|
||||
for (String id : roleInvalidations) {
|
||||
cache.invalidateRoleById(id);
|
||||
}
|
||||
for (String id : groupInvalidations) {
|
||||
cache.invalidateGroupById(id);
|
||||
}
|
||||
for (String id : appInvalidations) {
|
||||
cache.invalidateClientById(id);
|
||||
}
|
||||
for (String id : clientTemplateInvalidations) {
|
||||
cache.invalidateClientTemplateById(id);
|
||||
}
|
||||
}
|
||||
|
||||
private KeycloakTransaction getTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
transactionActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (delegate == null) return;
|
||||
if (clearAll) {
|
||||
cache.clear();
|
||||
}
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
setRollbackOnly = true;
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return setRollbackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transactionActive;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
RealmModel realm = getDelegate().createRealm(name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
RealmModel realm = getDelegate().createRealm(id, name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm(String id) {
|
||||
CachedRealm cached = cache.getRealm(id);
|
||||
if (cached == null) {
|
||||
RealmModel model = getDelegate().getRealm(id);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(id)) return model;
|
||||
cached = new CachedRealm(cache, this, model);
|
||||
cache.addRealm(cached);
|
||||
} else if (realmInvalidations.contains(id)) {
|
||||
return getDelegate().getRealm(id);
|
||||
} else if (managedRealms.containsKey(id)) {
|
||||
return managedRealms.get(id);
|
||||
}
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealmByName(String name) {
|
||||
CachedRealm cached = cache.getRealmByName(name);
|
||||
if (cached == null) {
|
||||
RealmModel model = getDelegate().getRealmByName(name);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(model.getId())) return model;
|
||||
cached = new CachedRealm(cache, this, model);
|
||||
cache.addRealm(cached);
|
||||
} else if (realmInvalidations.contains(cached.getId())) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
} else if (managedRealms.containsKey(cached.getId())) {
|
||||
return managedRealms.get(cached.getId());
|
||||
}
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(cached.getId(), adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RealmModel> getRealms() {
|
||||
// Retrieve realms from backend
|
||||
List<RealmModel> backendRealms = getDelegate().getRealms();
|
||||
|
||||
// Return cache delegates to ensure cache invalidated during write operations
|
||||
List<RealmModel> cachedRealms = new LinkedList<RealmModel>();
|
||||
for (RealmModel realm : backendRealms) {
|
||||
RealmModel cached = getRealm(realm.getId());
|
||||
cachedRealms.add(cached);
|
||||
}
|
||||
return cachedRealms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRealm(String id) {
|
||||
cache.invalidateRealmById(id);
|
||||
|
||||
RealmModel realm = getDelegate().getRealm(id);
|
||||
Set<RoleModel> realmRoles = null;
|
||||
if (realm != null) {
|
||||
realmRoles = realm.getRoles();
|
||||
}
|
||||
|
||||
boolean didIt = getDelegate().removeRealm(id);
|
||||
realmInvalidations.add(id);
|
||||
|
||||
// TODO: Temporary workaround to invalidate cached realm roles
|
||||
if (didIt && realmRoles != null) {
|
||||
for (RoleModel role : realmRoles) {
|
||||
roleInvalidations.add(role.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return didIt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != null) delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id, RealmModel realm) {
|
||||
CachedRole cached = cache.getRole(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
RoleModel model = getDelegate().getRoleById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (roleInvalidations.contains(id)) return model;
|
||||
if (model.getContainer() instanceof ClientModel) {
|
||||
cached = new CachedClientRole(((ClientModel) model.getContainer()).getId(), model, realm);
|
||||
} else {
|
||||
cached = new CachedRealmRole(model, realm);
|
||||
}
|
||||
cache.addRole(cached);
|
||||
|
||||
} else if (roleInvalidations.contains(id)) {
|
||||
return getDelegate().getRoleById(id, realm);
|
||||
} else if (managedRoles.containsKey(id)) {
|
||||
return managedRoles.get(id);
|
||||
}
|
||||
RoleAdapter adapter = new RoleAdapter(cached, cache, this, realm);
|
||||
managedRoles.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||
CachedGroup cached = cache.getGroup(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
GroupModel model = getDelegate().getGroupById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (groupInvalidations.contains(id)) return model;
|
||||
cached = new CachedGroup(realm, model);
|
||||
cache.addGroup(cached);
|
||||
|
||||
} else if (groupInvalidations.contains(id)) {
|
||||
return getDelegate().getGroupById(id, realm);
|
||||
} else if (managedGroups.containsKey(id)) {
|
||||
return managedGroups.get(id);
|
||||
}
|
||||
GroupAdapter adapter = new GroupAdapter(cached, this, session, realm);
|
||||
managedGroups.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
CachedClient cached = cache.getClient(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
ClientModel model = getDelegate().getClientById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (appInvalidations.contains(id)) return model;
|
||||
cached = new CachedClient(cache, getDelegate(), realm, model);
|
||||
cache.addClient(cached);
|
||||
} else if (appInvalidations.contains(id)) {
|
||||
return getDelegate().getClientById(id, realm);
|
||||
} else if (managedApplications.containsKey(id)) {
|
||||
return managedApplications.get(id);
|
||||
}
|
||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache);
|
||||
managedApplications.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||
return getDelegate().getClientByClientId(clientId, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeClient(String id, RealmModel realm) {
|
||||
ClientModel client = getClientById(id, realm);
|
||||
if (client == null) return false;
|
||||
registerApplicationInvalidation(id);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
cache.invalidateClientById(id);
|
||||
cache.invalidateRealmById(realm.getId());
|
||||
|
||||
|
||||
|
||||
Set<RoleModel> roles = client.getRoles();
|
||||
for (RoleModel role : roles) {
|
||||
registerRoleInvalidation(role.getId());
|
||||
}
|
||||
return getDelegate().removeClient(id, realm);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) {
|
||||
CachedClientTemplate cached = cache.getClientTemplate(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (clientTemplateInvalidations.contains(id)) return model;
|
||||
cached = new CachedClientTemplate(cache, getDelegate(), realm, model);
|
||||
cache.addClientTemplate(cached);
|
||||
} else if (clientTemplateInvalidations.contains(id)) {
|
||||
return getDelegate().getClientTemplateById(id, realm);
|
||||
} else if (managedClientTemplates.containsKey(id)) {
|
||||
return managedClientTemplates.get(id);
|
||||
}
|
||||
ClientTemplateModel adapter = new ClientTemplateAdapter(realm, cached, this, cache);
|
||||
managedClientTemplates.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
}
|
|
@ -19,8 +19,7 @@ package org.keycloak.models.cache.infinispan;
|
|||
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.cache.CacheUserProvider;
|
||||
import org.keycloak.models.cache.UserCache;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedUser;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedGroup;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
|
|
@ -18,15 +18,6 @@
|
|||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.notifications.Listener;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
|
@ -34,10 +25,7 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.CacheRealmProviderFactory;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -47,23 +35,21 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa
|
|||
|
||||
private static final Logger log = Logger.getLogger(InfinispanCacheRealmProviderFactory.class);
|
||||
|
||||
protected volatile InfinispanRealmCache realmCache;
|
||||
|
||||
protected final ConcurrentHashMap<String, String> realmLookup = new ConcurrentHashMap<>();
|
||||
protected volatile StreamRealmCache realmCache;
|
||||
|
||||
@Override
|
||||
public CacheRealmProvider create(KeycloakSession session) {
|
||||
lazyInit(session);
|
||||
return new DefaultCacheRealmProvider(realmCache, session);
|
||||
return new StreamCacheRealmProvider(realmCache, session);
|
||||
}
|
||||
|
||||
private void lazyInit(KeycloakSession session) {
|
||||
if (realmCache == null) {
|
||||
synchronized (this) {
|
||||
if (realmCache == null) {
|
||||
Cache<String, Object> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
|
||||
cache.addListener(new CacheListener());
|
||||
realmCache = new InfinispanRealmCache(cache, realmLookup);
|
||||
Cache<String, Revisioned> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
|
||||
Cache<String, Long> revisions = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.VERSION_CACHE_NAME);
|
||||
realmCache = new StreamRealmCache(cache, revisions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -84,77 +70,7 @@ public class InfinispanCacheRealmProviderFactory implements CacheRealmProviderFa
|
|||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "infinispan";
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Listener
|
||||
public class CacheListener {
|
||||
|
||||
@CacheEntryCreated
|
||||
public void created(CacheEntryCreatedEvent<String, Object> event) {
|
||||
if (!event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm realm = (CachedRealm) object;
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
log.tracev("Realm added realm={0}", realm.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryRemoved
|
||||
public void removed(CacheEntryRemovedEvent<String, Object> event) {
|
||||
if (event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryInvalidated
|
||||
public void removed(CacheEntryInvalidatedEvent<String, Object> event) {
|
||||
if (event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntriesEvicted
|
||||
public void userEvicted(CacheEntriesEvictedEvent<String, Object> event) {
|
||||
for (Object object : event.getEntries().values()) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(Object object) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm realm = (CachedRealm) object;
|
||||
|
||||
realmLookup.remove(realm.getName());
|
||||
|
||||
for (String r : realm.getRealmRoles().values()) {
|
||||
realmCache.evictRoleById(r);
|
||||
}
|
||||
|
||||
for (String c : realm.getClients()) {
|
||||
realmCache.evictClientById(c);
|
||||
}
|
||||
|
||||
log.tracev("Realm removed realm={0}", realm.getName());
|
||||
} else if (object instanceof CachedClient) {
|
||||
CachedClient client = (CachedClient) object;
|
||||
|
||||
for (String r : client.getRoles().values()) {
|
||||
realmCache.evictRoleById(r);
|
||||
}
|
||||
|
||||
log.tracev("Client removed client={0}", client.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.cache.CacheUserProvider;
|
||||
import org.keycloak.models.cache.CacheUserProviderFactory;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedUser;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
@ -78,7 +78,7 @@ public class InfinispanCacheUserProviderFactory implements CacheUserProviderFact
|
|||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "infinispan";
|
||||
return "default";
|
||||
}
|
||||
|
||||
@Listener
|
||||
|
|
|
@ -1,202 +0,0 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class InfinispanRealmCache implements RealmCache {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(InfinispanRealmCache.class);
|
||||
|
||||
protected final Cache<String, Object> cache;
|
||||
protected final ConcurrentHashMap<String, String> realmLookup;
|
||||
|
||||
public InfinispanRealmCache(Cache<String, Object> cache, ConcurrentHashMap<String, String> realmLookup) {
|
||||
this.cache = cache;
|
||||
this.realmLookup = realmLookup;
|
||||
}
|
||||
|
||||
public Cache<String, Object> getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getRealm(String id) {
|
||||
return get(id, CachedRealm.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRealm(CachedRealm realm) {
|
||||
logger.tracev("Invalidating realm {0}", realm.getId());
|
||||
cache.remove(realm.getId());
|
||||
realmLookup.remove(realm.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRealmById(String id) {
|
||||
CachedRealm cached = (CachedRealm) cache.remove(id);
|
||||
if (cached != null) realmLookup.remove(cached.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRealm(CachedRealm realm) {
|
||||
logger.tracev("Adding realm {0}", realm.getId());
|
||||
cache.putForExternalRead(realm.getId(), realm);
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getRealmByName(String name) {
|
||||
String id = realmLookup.get(name);
|
||||
return id != null ? getRealm(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedClient getClient(String id) {
|
||||
return get(id, CachedClient.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClient(CachedClient app) {
|
||||
logger.tracev("Removing application {0}", app.getId());
|
||||
cache.remove(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClient(CachedClient app) {
|
||||
logger.tracev("Adding application {0}", app.getId());
|
||||
cache.putForExternalRead(app.getId(), app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClientById(String id) {
|
||||
logger.tracev("Removing application {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictClientById(String id) {
|
||||
logger.tracev("Evicting application {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedGroup getGroup(String id) {
|
||||
return get(id, CachedGroup.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroup(CachedGroup role) {
|
||||
logger.tracev("Removing group {0}", role.getId());
|
||||
cache.remove(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addGroup(CachedGroup role) {
|
||||
logger.tracev("Adding group {0}", role.getId());
|
||||
cache.putForExternalRead(role.getId(), role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroupById(String id) {
|
||||
logger.tracev("Removing group {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRole getRole(String id) {
|
||||
return get(id, CachedRole.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRole(CachedRole role) {
|
||||
logger.tracev("Removing role {0}", role.getId());
|
||||
cache.remove(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictRoleById(String id) {
|
||||
logger.tracev("Evicting role {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRole(CachedRole role) {
|
||||
logger.tracev("Adding role {0}", role.getId());
|
||||
cache.putForExternalRead(role.getId(), role);
|
||||
}
|
||||
|
||||
private <T> T get(String id, Class<T> type) {
|
||||
Object o = cache.get(id);
|
||||
return o != null && type.isInstance(o) ? type.cast(o) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedClientTemplate getClientTemplate(String id) {
|
||||
return get(id, CachedClientTemplate.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Removing client template {0}", app.getId());
|
||||
cache.remove(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Adding client template {0}", app.getId());
|
||||
cache.putForExternalRead(app.getId(), app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClientTemplateById(String id) {
|
||||
logger.tracev("Removing client template {0}", id);
|
||||
cache.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictClientTemplateById(String id) {
|
||||
logger.tracev("Evicting client template {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -19,15 +19,14 @@ package org.keycloak.models.cache.infinispan;
|
|||
|
||||
import org.infinispan.Cache;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.UserCache;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedUser;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class InfinispanUserCache implements UserCache {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(InfinispanRealmCache.class);
|
||||
protected static final Logger logger = Logger.getLogger(InfinispanUserCache.class);
|
||||
|
||||
protected volatile boolean enabled = true;
|
||||
|
||||
|
|
|
@ -21,8 +21,7 @@ import org.keycloak.Config;
|
|||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.security.Key;
|
||||
|
@ -582,47 +581,37 @@ public class RealmAdapter implements RealmModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateDefaultRoles(String[] defaultRoles) {
|
||||
public void updateDefaultRoles(String... defaultRoles) {
|
||||
getDelegateForUpdate();
|
||||
updated.updateDefaultRoles(defaultRoles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDefaultRoles(String... defaultRoles) {
|
||||
getDelegateForUpdate();
|
||||
updated.removeDefaultRoles(defaultRoles);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientModel> getClients() {
|
||||
if (updated != null) return updated.getClients();
|
||||
List<ClientModel> apps = new LinkedList<>();
|
||||
for (String id : cached.getClients()) {
|
||||
ClientModel model = cacheSession.getClientById(id, this);
|
||||
if (model == null) {
|
||||
throw new IllegalStateException("Cached application not found: " + id);
|
||||
}
|
||||
apps.add(model);
|
||||
}
|
||||
return Collections.unmodifiableList(apps);
|
||||
return cacheSession.getClients(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(String name) {
|
||||
getDelegateForUpdate();
|
||||
ClientModel app = updated.addClient(name);
|
||||
cacheSession.registerApplicationInvalidation(app.getId());
|
||||
return app;
|
||||
return cacheSession.addClient(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(String id, String clientId) {
|
||||
getDelegateForUpdate();
|
||||
ClientModel app = updated.addClient(id, clientId);
|
||||
cacheSession.registerApplicationInvalidation(app.getId());
|
||||
return app;
|
||||
return cacheSession.addClient(this, id, clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeClient(String id) {
|
||||
cacheSession.registerApplicationInvalidation(id);
|
||||
getDelegateForUpdate();
|
||||
return updated.removeClient(id);
|
||||
return cacheSession.removeClient(id, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -888,12 +877,18 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public RoleModel getRole(String name) {
|
||||
if (updated != null) return updated.getRole(name);
|
||||
String id = cached.getRealmRoles().get(name);
|
||||
if (id == null) return null;
|
||||
return cacheSession.getRoleById(id, this);
|
||||
for (RoleModel role : getRoles()) {
|
||||
if (role.getName().equals(name)) return role;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles() {
|
||||
return cacheSession.getRealmRoles(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
getDelegateForUpdate();
|
||||
|
@ -917,18 +912,6 @@ public class RealmAdapter implements RealmModel {
|
|||
return updated.removeRole(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles() {
|
||||
if (updated != null) return updated.getRoles();
|
||||
|
||||
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||
for (String id : cached.getRealmRoles().values()) {
|
||||
RoleModel roleById = cacheSession.getRoleById(id, this);
|
||||
if (roleById == null) continue;
|
||||
roles.add(roleById);
|
||||
}
|
||||
return Collections.unmodifiableSet(roles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIdentityFederationEnabled() {
|
||||
|
|
|
@ -15,13 +15,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache;
|
||||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClient;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRole;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
@ -21,10 +21,9 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRole;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.HashSet;
|
||||
|
@ -38,13 +37,11 @@ public class RoleAdapter implements RoleModel {
|
|||
|
||||
protected RoleModel updated;
|
||||
protected CachedRole cached;
|
||||
protected RealmCache cache;
|
||||
protected CacheRealmProvider cacheSession;
|
||||
protected RealmModel realm;
|
||||
|
||||
public RoleAdapter(CachedRole cached, RealmCache cache, CacheRealmProvider session, RealmModel realm) {
|
||||
public RoleAdapter(CachedRole cached, CacheRealmProvider session, RealmModel realm) {
|
||||
this.cached = cached;
|
||||
this.cache = cache;
|
||||
this.cacheSession = session;
|
||||
this.realm = realm;
|
||||
}
|
||||
|
@ -125,7 +122,7 @@ public class RoleAdapter implements RoleModel {
|
|||
for (String id : cached.getComposites()) {
|
||||
RoleModel role = realm.getRoleById(id);
|
||||
if (role == null) {
|
||||
throw new IllegalStateException("Could not find composite: " + id);
|
||||
throw new IllegalStateException("Could not find composite in role " + getName() + ": " + id);
|
||||
}
|
||||
set.add(role);
|
||||
}
|
||||
|
@ -138,7 +135,7 @@ public class RoleAdapter implements RoleModel {
|
|||
return realm;
|
||||
} else {
|
||||
CachedClientRole appRole = (CachedClientRole)cached;
|
||||
return realm.getClientById(appRole.getIdClient());
|
||||
return realm.getClientById(appRole.getClientId());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
776
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java
vendored
Executable file
776
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamCacheRealmProvider.java
vendored
Executable file
|
@ -0,0 +1,776 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClient;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.ClientListQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.RealmListQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.RoleListQuery;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* - the high level architecture of this cache is an invalidation cache.
|
||||
* - the cache is manual/custom versioned. When a model is updated, we remove it from the cache
|
||||
* which causes an invalidation message to be sent across the cluster.
|
||||
* - We had to do it this way because Infinispan REPEATABLE_READ
|
||||
* wouldn't cut it in invalidation mode. Also, REPEATABLE_READ doesn't work very well on relationships and items that are
|
||||
* not in the cache.
|
||||
* - There are two Infinispan caches. One clustered that holds actual objects and a another local one that holds revision
|
||||
* numbers of cached objects. Whenever a cached object is removed (invalidated), the local revision
|
||||
* cache number or that key is bumped higher based on a local version counter. Whenever a cache entry is fetched, this
|
||||
* revision number is also fetched and compared against the revision number in the cache entry to see if the cache entry
|
||||
* is stale. Whenever a cache entry is added, this revision number is also checked against the revision cache.
|
||||
* - Revision entries are actually never removed (although they could be evicted by cache eviction policies). The reason for this
|
||||
* is that it is possible for a stale object to be inserted if one thread loads and the data is updated in the database before
|
||||
* it is added to the cache. So, we keep the version number around for this.
|
||||
* - In a transaction, objects are registered to be invalidated. If an object is marked for invalidation within a transaction
|
||||
* a cached object should never be returned. An DB adapter should always be returned.
|
||||
* - At prepare phase of the transaction, a local lock on the revision cache will be obtained for each object marked for invalidation
|
||||
* we sort the list of these keys to order local acquisition and avoid deadlocks.
|
||||
* - After DB commits, the objects marked for invalidation are invalidated, or rather removed from the cache. At this time
|
||||
* the revision cache entry for this object has its version number bumped.
|
||||
* - Whenever an object is marked for invalidation, the cache is also searched for any objects that are related to this object
|
||||
* and need to also be evicted/removed. We use the Infinispan Stream SPI for this.
|
||||
*
|
||||
* ClientList caches:
|
||||
* - lists of clients are cached in a specific cache entry i.e. realm clients, find client by clientId
|
||||
* - realm client lists need to be invalidated and evited whenever a client is added or removed from a realm. RealmProvider
|
||||
* now has addClient/removeClient at its top level. All adapaters should use these methods so that the appropriate invalidations
|
||||
* can be registered.
|
||||
* - whenever a client is added/removed the realm of the client is added to a listInvalidations set
|
||||
* this set must be checked before sending back or caching a cached query. This check is required to
|
||||
* avoid caching an uncommitted removal/add in a query cache.
|
||||
* - when a client is removed, any queries that contain that client must also be removed.
|
||||
* - a client removal will also cause anything that is contained and cached within that client to be removed
|
||||
*
|
||||
* Clustered caches:
|
||||
* - There is a Infinispan @Listener registered. If an invalidation event happens, this is treated like
|
||||
* the object was removed from the database and will perform evictions based on that assumption.
|
||||
* - Eviction events will also cascade other evictions, but not assume this is a db removal.
|
||||
*
|
||||
* Groups and Roles:
|
||||
* - roles are tricky because of composites. Composite lists are cached too. So, when a role is removed
|
||||
* we also iterate and invalidate any role or group that contains that role being removed.
|
||||
*
|
||||
*
|
||||
*
|
||||
* - any relationship should be resolved from session.realms(). For example if JPA.getClientByClientId() is invoked,
|
||||
* JPA should find the id of the client and then call session.realms().getClientById(). THis is to ensure that the cached
|
||||
* object is invoked and all proper invalidation are being invoked.
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class StreamCacheRealmProvider implements CacheRealmProvider {
|
||||
protected static final Logger logger = Logger.getLogger(StreamCacheRealmProvider.class);
|
||||
public static final String REALM_CLIENTS_QUERY_SUFFIX = ".realm.clients";
|
||||
public static final String ROLES_QUERY_SUFFIX = ".roles";
|
||||
public static final String ROLE_BY_NAME_QUERY_SUFFIX = ".role.by-name";
|
||||
protected StreamRealmCache cache;
|
||||
protected KeycloakSession session;
|
||||
protected RealmProvider delegate;
|
||||
protected boolean transactionActive;
|
||||
protected boolean setRollbackOnly;
|
||||
|
||||
protected Map<String, RealmModel> managedRealms = new HashMap<>();
|
||||
protected Map<String, ClientModel> managedApplications = new HashMap<>();
|
||||
protected Map<String, ClientTemplateModel> managedClientTemplates = new HashMap<>();
|
||||
protected Map<String, RoleModel> managedRoles = new HashMap<>();
|
||||
protected Map<String, GroupModel> managedGroups = new HashMap<>();
|
||||
protected Set<String> listInvalidations = new HashSet<>();
|
||||
protected Set<String> invalidations = new HashSet<>();
|
||||
|
||||
protected boolean clearAll;
|
||||
|
||||
public StreamCacheRealmProvider(StreamRealmCache cache, KeycloakSession session) {
|
||||
this.cache = cache;
|
||||
this.session = session;
|
||||
session.getTransaction().enlistPrepare(getPrepareTransaction());
|
||||
session.getTransaction().enlistAfterCompletion(getAfterTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return getDelegate().getMigrationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmProvider getDelegate() {
|
||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (delegate != null) return delegate;
|
||||
delegate = session.getProvider(RealmProvider.class);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRealmInvalidation(String id) {
|
||||
invalidations.add(id);
|
||||
cache.realmInvalidation(id, invalidations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerClientInvalidation(String id) {
|
||||
invalidations.add(id);
|
||||
cache.clientInvalidation(id, invalidations);
|
||||
}
|
||||
@Override
|
||||
public void registerClientTemplateInvalidation(String id) {
|
||||
invalidations.add(id);
|
||||
cache.clientTemplateInvalidation(id, invalidations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRoleInvalidation(String id) {
|
||||
invalidations.add(id);
|
||||
cache.roleInvalidation(id, invalidations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerGroupInvalidation(String id) {
|
||||
invalidations.add(id);
|
||||
cache.groupInvalidation(id, invalidations);
|
||||
}
|
||||
|
||||
protected void runInvalidations() {
|
||||
for (String id : invalidations) {
|
||||
cache.invalidateObject(id);
|
||||
}
|
||||
}
|
||||
|
||||
private KeycloakTransaction getPrepareTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
transactionActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (delegate == null) return;
|
||||
List<String> locks = new LinkedList<>();
|
||||
locks.addAll(invalidations);
|
||||
|
||||
Collections.sort(locks); // lock ordering
|
||||
cache.getRevisions().startBatch();
|
||||
//if (!invalidates.isEmpty()) cache.getRevisions().getAdvancedCache().lock(invalidates);
|
||||
for (String lock : locks) {
|
||||
boolean success = cache.getRevisions().getAdvancedCache().lock(lock);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
setRollbackOnly = true;
|
||||
transactionActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return setRollbackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transactionActive;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private KeycloakTransaction getAfterTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
transactionActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
try {
|
||||
if (delegate == null) return;
|
||||
if (clearAll) {
|
||||
cache.clear();
|
||||
}
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
} finally {
|
||||
cache.endRevisionBatch();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
try {
|
||||
setRollbackOnly = true;
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
} finally {
|
||||
cache.endRevisionBatch();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return setRollbackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transactionActive;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
RealmModel realm = getDelegate().createRealm(name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
RealmModel realm = getDelegate().createRealm(id, name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm(String id) {
|
||||
CachedRealm cached = cache.get(id, CachedRealm.class);
|
||||
if (cached != null) {
|
||||
logger.tracev("by id cache hit: {0}", cached.getName());
|
||||
}
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
RealmModel model = getDelegate().getRealm(id);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
cached = new CachedRealm(loaded, model);
|
||||
cache.addRevisioned(cached);
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getRealm(id);
|
||||
} else if (managedRealms.containsKey(id)) {
|
||||
return managedRealms.get(id);
|
||||
}
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealmByName(String name) {
|
||||
String cacheKey = getRealmByNameCacheKey(name);
|
||||
RealmListQuery query = cache.get(cacheKey, RealmListQuery.class);
|
||||
if (query != null) {
|
||||
logger.tracev("realm by name cache hit: {0}", name);
|
||||
}
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
RealmModel model = getDelegate().getRealmByName(name);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(model.getId())) return model;
|
||||
query = new RealmListQuery(loaded, cacheKey, model.getId());
|
||||
cache.addRevisioned(query);
|
||||
return model;
|
||||
} else if (invalidations.contains(cacheKey)) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
} else {
|
||||
String realmId = query.getRealms().iterator().next();
|
||||
if (invalidations.contains(realmId)) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
}
|
||||
return getRealm(realmId);
|
||||
}
|
||||
}
|
||||
|
||||
public String getRealmByNameCacheKey(String name) {
|
||||
return "realm.query.by.name." + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RealmModel> getRealms() {
|
||||
// Retrieve realms from backend
|
||||
List<RealmModel> backendRealms = getDelegate().getRealms();
|
||||
|
||||
// Return cache delegates to ensure cache invalidated during write operations
|
||||
List<RealmModel> cachedRealms = new LinkedList<RealmModel>();
|
||||
for (RealmModel realm : backendRealms) {
|
||||
RealmModel cached = getRealm(realm.getId());
|
||||
cachedRealms.add(cached);
|
||||
}
|
||||
return cachedRealms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRealm(String id) {
|
||||
RealmModel realm = getRealm(id);
|
||||
if (realm == null) return false;
|
||||
|
||||
invalidations.add(getRealmClientsQueryCacheKey(id));
|
||||
invalidations.add(getRealmByNameCacheKey(realm.getName()));
|
||||
cache.invalidateObject(id);
|
||||
cache.realmRemoval(id, invalidations);
|
||||
return getDelegate().removeRealm(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String clientId) {
|
||||
ClientModel client = getDelegate().addClient(realm, clientId);
|
||||
return addedClient(realm, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String id, String clientId) {
|
||||
ClientModel client = getDelegate().addClient(realm, id, clientId);
|
||||
return addedClient(realm, client);
|
||||
}
|
||||
|
||||
private ClientModel addedClient(RealmModel realm, ClientModel client) {
|
||||
logger.trace("added Client.....");
|
||||
// need to invalidate realm client query cache every time as it may not be loaded on this node, but loaded on another
|
||||
invalidations.add(getRealmClientsQueryCacheKey(realm.getId()));
|
||||
invalidations.add(client.getId());
|
||||
cache.clientAdded(realm.getId(), client.getId(), invalidations);
|
||||
// this is needed so that a new client that hasn't been committed isn't cached in a query
|
||||
listInvalidations.add(realm.getId());
|
||||
return client;
|
||||
}
|
||||
|
||||
private String getRealmClientsQueryCacheKey(String realm) {
|
||||
return realm + REALM_CLIENTS_QUERY_SUFFIX;
|
||||
}
|
||||
|
||||
private String getRolesCacheKey(String container) {
|
||||
return container + ROLES_QUERY_SUFFIX;
|
||||
}
|
||||
private String getRoleByNameCacheKey(String container, String name) {
|
||||
return container + "." + name + ROLES_QUERY_SUFFIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientModel> getClients(RealmModel realm) {
|
||||
String cacheKey = getRealmClientsQueryCacheKey(realm.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getClients(realm);
|
||||
}
|
||||
|
||||
ClientListQuery query = cache.get(cacheKey, ClientListQuery.class);
|
||||
if (query != null) {
|
||||
logger.tracev("getClients cache hit: {0}", realm.getName());
|
||||
}
|
||||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
List<ClientModel> model = getDelegate().getClients(realm);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (ClientModel client : model) ids.add(client.getId());
|
||||
query = new ClientListQuery(loaded, cacheKey, realm, ids);
|
||||
logger.tracev("adding realm clients cache miss: realm {0} key {1}", realm.getName(), cacheKey);
|
||||
cache.addRevisioned(query);
|
||||
return model;
|
||||
}
|
||||
List<ClientModel> list = new LinkedList<>();
|
||||
for (String id : query.getClients()) {
|
||||
ClientModel client = session.realms().getClientById(id, realm);
|
||||
if (client == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getClients(realm);
|
||||
}
|
||||
list.add(client);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean removeClient(String id, RealmModel realm) {
|
||||
ClientModel client = getClientById(id, realm);
|
||||
if (client == null) return false;
|
||||
// need to invalidate realm client query cache every time client list is changed
|
||||
invalidations.add(getRealmClientsQueryCacheKey(realm.getId()));
|
||||
invalidations.add(getClientByClientIdCacheKey(client.getClientId(), realm));
|
||||
listInvalidations.add(realm.getId());
|
||||
registerClientInvalidation(id);
|
||||
cache.clientRemoval(realm.getId(), id, invalidations);
|
||||
for (RoleModel role : client.getRoles()) {
|
||||
cache.roleInvalidation(role.getId(), invalidations);
|
||||
}
|
||||
return getDelegate().removeClient(id, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != null) delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRealmRole(RealmModel realm, String name) {
|
||||
return addRealmRole(realm, KeycloakModelUtils.generateId(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRealmRole(RealmModel realm, String id, String name) {
|
||||
invalidations.add(getRolesCacheKey(realm.getId()));
|
||||
// this is needed so that a new role that hasn't been committed isn't cached in a query
|
||||
listInvalidations.add(realm.getId());
|
||||
RoleModel role = getDelegate().addRealmRole(realm, name);
|
||||
invalidations.add(role.getId());
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRealmRoles(RealmModel realm) {
|
||||
String cacheKey = getRolesCacheKey(realm.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getRealmRoles(realm);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
if (query != null) {
|
||||
logger.tracev("getRealmRoles cache hit: {0}", realm.getName());
|
||||
}
|
||||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
Set<RoleModel> model = getDelegate().getRealmRoles(realm);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (RoleModel role : model) ids.add(role.getId());
|
||||
query = new RoleListQuery(loaded, cacheKey, realm, ids);
|
||||
logger.tracev("adding realm roles cache miss: realm {0} key {1}", realm.getName(), cacheKey);
|
||||
cache.addRevisioned(query);
|
||||
return model;
|
||||
}
|
||||
Set<RoleModel> list = new HashSet<>();
|
||||
for (String id : query.getRoles()) {
|
||||
RoleModel role = session.realms().getRoleById(id, realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getRealmRoles(realm);
|
||||
}
|
||||
list.add(role);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
|
||||
String cacheKey = getRolesCacheKey(client.getId());
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getClientRoles(realm, client);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
if (query != null) {
|
||||
logger.tracev("getClientRoles cache hit: {0}", client.getClientId());
|
||||
}
|
||||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
Set<RoleModel> model = getDelegate().getClientRoles(realm, client);
|
||||
if (model == null) return null;
|
||||
Set<String> ids = new HashSet<>();
|
||||
for (RoleModel role : model) ids.add(role.getId());
|
||||
query = new RoleListQuery(loaded, cacheKey, realm, ids, client.getClientId());
|
||||
logger.tracev("adding client roles cache miss: client {0} key {1}", client.getClientId(), cacheKey);
|
||||
cache.addRevisioned(query);
|
||||
return model;
|
||||
}
|
||||
Set<RoleModel> list = new HashSet<>();
|
||||
for (String id : query.getRoles()) {
|
||||
RoleModel role = session.realms().getRoleById(id, realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getClientRoles(realm, client);
|
||||
}
|
||||
list.add(role);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
|
||||
invalidations.add(getRolesCacheKey(client.getId()));
|
||||
// this is needed so that a new role that hasn't been committed isn't cached in a query
|
||||
listInvalidations.add(client.getId());
|
||||
RoleModel role = getDelegate().addClientRole(realm, client, id, name);
|
||||
invalidations.add(role.getId());
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRealmRole(RealmModel realm, String name) {
|
||||
String cacheKey = getRoleByNameCacheKey(realm.getId(), name);
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(realm.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getRealmRole(realm, name);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
if (query != null) {
|
||||
logger.tracev("getRealmRole cache hit: {0}.{1}", realm.getName(), name);
|
||||
}
|
||||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
RoleModel model = getDelegate().getRealmRole(realm, name);
|
||||
if (model == null) return null;
|
||||
query = new RoleListQuery(loaded, cacheKey, realm, model.getId());
|
||||
logger.tracev("adding realm role cache miss: client {0} key {1}", realm.getName(), cacheKey);
|
||||
cache.addRevisioned(query);
|
||||
return model;
|
||||
}
|
||||
RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getRealmRole(realm, name);
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
String cacheKey = getRoleByNameCacheKey(client.getId(), name);
|
||||
boolean queryDB = invalidations.contains(cacheKey) || listInvalidations.contains(client.getId());
|
||||
if (queryDB) {
|
||||
return getDelegate().getClientRole(realm, client, name);
|
||||
}
|
||||
|
||||
RoleListQuery query = cache.get(cacheKey, RoleListQuery.class);
|
||||
if (query != null) {
|
||||
logger.tracev("getClientRole cache hit: {0}.{1}", client.getClientId(), name);
|
||||
}
|
||||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
RoleModel model = getDelegate().getClientRole(realm, client, name);
|
||||
if (model == null) return null;
|
||||
query = new RoleListQuery(loaded, cacheKey, realm, model.getId(), client.getClientId());
|
||||
logger.tracev("adding client role cache miss: client {0} key {1}", client.getClientId(), cacheKey);
|
||||
cache.addRevisioned(query);
|
||||
return model;
|
||||
}
|
||||
RoleModel role = getRoleById(query.getRoles().iterator().next(), realm);
|
||||
if (role == null) {
|
||||
invalidations.add(cacheKey);
|
||||
return getDelegate().getClientRole(realm, client, name);
|
||||
}
|
||||
return role;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRole(RealmModel realm, RoleModel role) {
|
||||
invalidations.add(getRolesCacheKey(role.getContainer().getId()));
|
||||
invalidations.add(getRoleByNameCacheKey(role.getContainer().getId(), role.getName()));
|
||||
listInvalidations.add(role.getContainer().getId());
|
||||
invalidations.add(role.getId());
|
||||
return getDelegate().removeRole(realm, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id, RealmModel realm) {
|
||||
CachedRole cached = cache.get(id, CachedRole.class);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
RoleModel model = getDelegate().getRoleById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
if (model.getContainer() instanceof ClientModel) {
|
||||
cached = new CachedClientRole(loaded, ((ClientModel) model.getContainer()).getId(), model, realm);
|
||||
} else {
|
||||
cached = new CachedRealmRole(loaded, model, realm);
|
||||
}
|
||||
cache.addRevisioned(cached);
|
||||
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getRoleById(id, realm);
|
||||
} else if (managedRoles.containsKey(id)) {
|
||||
return managedRoles.get(id);
|
||||
}
|
||||
RoleAdapter adapter = new RoleAdapter(cached,this, realm);
|
||||
managedRoles.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||
CachedGroup cached = cache.get(id, CachedGroup.class);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
GroupModel model = getDelegate().getGroupById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
cached = new CachedGroup(loaded, realm, model);
|
||||
cache.addRevisioned(cached);
|
||||
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getGroupById(id, realm);
|
||||
} else if (managedGroups.containsKey(id)) {
|
||||
return managedGroups.get(id);
|
||||
}
|
||||
GroupAdapter adapter = new GroupAdapter(cached, this, session, realm);
|
||||
managedGroups.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
CachedClient cached = cache.get(id, CachedClient.class);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
if (cached != null) {
|
||||
logger.tracev("client by id cache hit: {0}", cached.getClientId());
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
ClientModel model = getDelegate().getClientById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
cached = new CachedClient(loaded, realm, model);
|
||||
logger.tracev("adding client by id cache miss: {0}", cached.getClientId());
|
||||
cache.addRevisioned(cached);
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getClientById(id, realm);
|
||||
} else if (managedApplications.containsKey(id)) {
|
||||
return managedApplications.get(id);
|
||||
}
|
||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this, null);
|
||||
managedApplications.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||
String cacheKey = getClientByClientIdCacheKey(clientId, realm);
|
||||
ClientListQuery query = cache.get(cacheKey, ClientListQuery.class);
|
||||
String id = null;
|
||||
|
||||
if (query != null) {
|
||||
logger.tracev("client by name cache hit: {0}", clientId);
|
||||
}
|
||||
|
||||
if (query == null) {
|
||||
Long loaded = cache.getCurrentRevision(cacheKey);
|
||||
ClientModel model = getDelegate().getClientByClientId(clientId, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(model.getId())) return model;
|
||||
id = model.getId();
|
||||
query = new ClientListQuery(loaded, cacheKey, realm, id);
|
||||
logger.tracev("adding client by name cache miss: {0}", clientId);
|
||||
cache.addRevisioned(query);
|
||||
} else if (invalidations.contains(cacheKey)) {
|
||||
return getDelegate().getClientByClientId(clientId, realm);
|
||||
} else {
|
||||
id = query.getClients().iterator().next();
|
||||
if (invalidations.contains(id)) {
|
||||
return getDelegate().getClientByClientId(clientId, realm);
|
||||
}
|
||||
}
|
||||
return getClientById(id, realm);
|
||||
}
|
||||
|
||||
public String getClientByClientIdCacheKey(String clientId, RealmModel realm) {
|
||||
return realm.getId() + ".client.query.by.clientId." + clientId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) {
|
||||
CachedClientTemplate cached = cache.get(id, CachedClientTemplate.class);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (invalidations.contains(id)) return model;
|
||||
cached = new CachedClientTemplate(loaded, realm, model);
|
||||
cache.addRevisioned(cached);
|
||||
} else if (invalidations.contains(id)) {
|
||||
return getDelegate().getClientTemplateById(id, realm);
|
||||
} else if (managedClientTemplates.containsKey(id)) {
|
||||
return managedClientTemplates.get(id);
|
||||
}
|
||||
ClientTemplateModel adapter = new ClientTemplateAdapter(realm, cached, this);
|
||||
managedClientTemplates.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
}
|
304
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java
vendored
Executable file
304
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/StreamRealmCache.java
vendored
Executable file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.notifications.Listener;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClient;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.stream.ClientQueryPredicate;
|
||||
import org.keycloak.models.cache.infinispan.stream.ClientTemplateQueryPredicate;
|
||||
import org.keycloak.models.cache.infinispan.stream.GroupQueryPredicate;
|
||||
import org.keycloak.models.cache.infinispan.stream.HasRolePredicate;
|
||||
import org.keycloak.models.cache.infinispan.stream.InClientPredicate;
|
||||
import org.keycloak.models.cache.infinispan.stream.InRealmPredicate;
|
||||
import org.keycloak.models.cache.infinispan.stream.RealmQueryPredicate;
|
||||
import org.keycloak.models.cache.infinispan.stream.RoleQueryPredicate;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
@Listener
|
||||
public class StreamRealmCache {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(StreamRealmCache.class);
|
||||
|
||||
protected final Cache<String, Long> revisions;
|
||||
protected final Cache<String, Revisioned> cache;
|
||||
|
||||
public StreamRealmCache(Cache<String, Revisioned> cache, Cache<String, Long> revisions) {
|
||||
this.cache = cache;
|
||||
this.cache.addListener(this);
|
||||
this.revisions = revisions;
|
||||
}
|
||||
|
||||
public Cache<String, Revisioned> getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public Cache<String, Long> getRevisions() {
|
||||
return revisions;
|
||||
}
|
||||
|
||||
public Long getCurrentRevision(String id) {
|
||||
Long revision = revisions.get(id);
|
||||
if (revision == null) return UpdateCounter.current();
|
||||
return revision;
|
||||
}
|
||||
|
||||
public void endRevisionBatch() {
|
||||
try {
|
||||
revisions.endBatch(true);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public <T> T get(String id, Class<T> type) {
|
||||
Revisioned o = (Revisioned)cache.get(id);
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
Long rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
logger.tracev("get() missing rev");
|
||||
return null;
|
||||
}
|
||||
long oRev = o.getRevision() == null ? -1L : o.getRevision().longValue();
|
||||
if (rev > oRev) {
|
||||
logger.tracev("get() rev: {0} o.rev: {1}", rev.longValue(), oRev);
|
||||
return null;
|
||||
}
|
||||
return o != null && type.isInstance(o) ? type.cast(o) : null;
|
||||
}
|
||||
|
||||
public Object invalidateObject(String id) {
|
||||
Revisioned removed = (Revisioned)cache.remove(id);
|
||||
long next = UpdateCounter.next();
|
||||
Object rev = revisions.put(id, next);
|
||||
return removed;
|
||||
}
|
||||
|
||||
public void addRevisioned(Revisioned object) {
|
||||
//startRevisionBatch();
|
||||
String id = object.getId();
|
||||
try {
|
||||
//revisions.getAdvancedCache().lock(id);
|
||||
Long rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
if (id.endsWith("realm.clients")) logger.trace("addRevisioned rev == null realm.clients");
|
||||
rev = UpdateCounter.current();
|
||||
revisions.put(id, rev);
|
||||
}
|
||||
revisions.startBatch();
|
||||
if (!revisions.getAdvancedCache().lock(id)) {
|
||||
logger.trace("Could not obtain version lock");
|
||||
}
|
||||
rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
if (id.endsWith("realm.clients")) logger.trace("addRevisioned rev2 == null realm.clients");
|
||||
return;
|
||||
}
|
||||
if (rev.equals(object.getRevision())) {
|
||||
if (id.endsWith("realm.clients")) logger.tracev("adding Object.revision {0} rev {1}", object.getRevision(), rev);
|
||||
cache.putForExternalRead(id, object);
|
||||
return;
|
||||
}
|
||||
if (rev > object.getRevision()) { // revision is ahead, don't cache
|
||||
if (id.endsWith("realm.clients")) logger.trace("addRevisioned revision is ahead realm.clients");
|
||||
return;
|
||||
}
|
||||
// revisions cache has a lower value than the object.revision, so update revision and add it to cache
|
||||
if (id.endsWith("realm.clients")) logger.tracev("adding Object.revision {0} rev {1}", object.getRevision(), rev);
|
||||
revisions.put(id, object.getRevision());
|
||||
cache.putForExternalRead(id, object);
|
||||
} finally {
|
||||
endRevisionBatch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
public void realmInvalidation(String id, Set<String> invalidations) {
|
||||
Predicate<Map.Entry<String, Revisioned>> predicate = getRealmInvalidationPredicate(id);
|
||||
addInvalidations(predicate, invalidations);
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getRealmInvalidationPredicate(String id) {
|
||||
return RealmQueryPredicate.create().realm(id);
|
||||
}
|
||||
|
||||
public void clientInvalidation(String id, Set<String> invalidations) {
|
||||
addInvalidations(getClientInvalidationPredicate(id), invalidations);
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getClientInvalidationPredicate(String id) {
|
||||
return ClientQueryPredicate.create().client(id);
|
||||
}
|
||||
|
||||
public void roleInvalidation(String id, Set<String> invalidations) {
|
||||
addInvalidations(getRoleInvalidationPredicate(id), invalidations);
|
||||
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getRoleInvalidationPredicate(String id) {
|
||||
return HasRolePredicate.create().role(id);
|
||||
}
|
||||
|
||||
public void groupInvalidation(String id, Set<String> invalidations) {
|
||||
addInvalidations(getGroupInvalidationPredicate(id), invalidations);
|
||||
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getGroupInvalidationPredicate(String id) {
|
||||
return GroupQueryPredicate.create().group(id);
|
||||
}
|
||||
|
||||
public void clientTemplateInvalidation(String id, Set<String> invalidations) {
|
||||
addInvalidations(getClientTemplateInvalidationPredicate(id), invalidations);
|
||||
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getClientTemplateInvalidationPredicate(String id) {
|
||||
return ClientTemplateQueryPredicate.create().template(id);
|
||||
}
|
||||
|
||||
public void realmRemoval(String id, Set<String> invalidations) {
|
||||
Predicate<Map.Entry<String, Revisioned>> predicate = getRealmRemovalPredicate(id);
|
||||
addInvalidations(predicate, invalidations);
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getRealmRemovalPredicate(String id) {
|
||||
Predicate<Map.Entry<String, Revisioned>> predicate = null;
|
||||
predicate = RealmQueryPredicate.create().realm(id)
|
||||
.or(InRealmPredicate.create().realm(id));
|
||||
return predicate;
|
||||
}
|
||||
|
||||
public void clientAdded(String realmId, String id, Set<String> invalidations) {
|
||||
addInvalidations(getClientAddedPredicate(realmId), invalidations);
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getClientAddedPredicate(String realmId) {
|
||||
return ClientQueryPredicate.create().inRealm(realmId);
|
||||
}
|
||||
|
||||
public void clientRemoval(String realmId, String id, Set<String> invalidations) {
|
||||
Predicate<Map.Entry<String, Revisioned>> predicate = null;
|
||||
predicate = getClientRemovalPredicate(realmId, id);
|
||||
addInvalidations(predicate, invalidations);
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getClientRemovalPredicate(String realmId, String id) {
|
||||
Predicate<Map.Entry<String, Revisioned>> predicate;
|
||||
predicate = ClientQueryPredicate.create().inRealm(realmId)
|
||||
.or(ClientQueryPredicate.create().client(id))
|
||||
.or(InClientPredicate.create().client(id));
|
||||
return predicate;
|
||||
}
|
||||
|
||||
public void roleRemoval(String id, Set<String> invalidations) {
|
||||
addInvalidations(getRoleRemovalPredicate(id), invalidations);
|
||||
|
||||
}
|
||||
|
||||
public Predicate<Map.Entry<String, Revisioned>> getRoleRemovalPredicate(String id) {
|
||||
return getRoleInvalidationPredicate(id);
|
||||
}
|
||||
|
||||
public void addInvalidations(Predicate<Map.Entry<String, Revisioned>> predicate, Set<String> invalidations) {
|
||||
Iterator<Map.Entry<String, Revisioned>> it = getEntryIterator(predicate);
|
||||
while (it.hasNext()) {
|
||||
invalidations.add(it.next().getKey());
|
||||
}
|
||||
}
|
||||
|
||||
private Iterator<Map.Entry<String, Revisioned>> getEntryIterator(Predicate<Map.Entry<String, Revisioned>> predicate) {
|
||||
return cache
|
||||
.entrySet()
|
||||
.stream()
|
||||
.filter(predicate).iterator();
|
||||
}
|
||||
|
||||
@CacheEntryInvalidated
|
||||
public void cacheInvalidated(CacheEntryInvalidatedEvent<String, Object> event) {
|
||||
if (!event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
Predicate<Map.Entry<String, Revisioned>> predicate = getInvalidationPredicate(object);
|
||||
if (predicate != null) runEvictions(predicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntriesEvicted
|
||||
public void cacheEvicted(CacheEntriesEvictedEvent<String, Object> event) {
|
||||
if (!event.isPre())
|
||||
for (Object object : event.getEntries().values()) {
|
||||
Predicate<Map.Entry<String, Revisioned>> predicate = getInvalidationPredicate(object);
|
||||
if (predicate != null) runEvictions(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
public void runEvictions(Predicate<Map.Entry<String, Revisioned>> current) {
|
||||
Set<String> evictions = new HashSet<>();
|
||||
addInvalidations(current, evictions);
|
||||
for (String key : evictions) cache.evict(key);
|
||||
}
|
||||
|
||||
protected Predicate<Map.Entry<String, Revisioned>> getInvalidationPredicate(Object object) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm cached = (CachedRealm)object;
|
||||
return getRealmRemovalPredicate(cached.getId());
|
||||
} else if (object instanceof CachedClient) {
|
||||
CachedClient cached = (CachedClient)object;
|
||||
Predicate<Map.Entry<String, Revisioned>> predicate = getClientRemovalPredicate(cached.getRealm(), cached.getId());
|
||||
return predicate;
|
||||
} else if (object instanceof CachedRole) {
|
||||
CachedRole cached = (CachedRole)object;
|
||||
return getRoleRemovalPredicate(cached.getId());
|
||||
} else if (object instanceof CachedGroup) {
|
||||
CachedGroup cached = (CachedGroup)object;
|
||||
return getGroupInvalidationPredicate(cached.getId());
|
||||
} else if (object instanceof CachedClientTemplate) {
|
||||
CachedClientTemplate cached = (CachedClientTemplate)object;
|
||||
return getClientTemplateInvalidationPredicate(cached.getId());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.models.cache.infinispan.locking;
|
||||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
|
@ -19,7 +19,7 @@ package org.keycloak.models.cache.infinispan;
|
|||
|
||||
import org.keycloak.models.*;
|
||||
import org.keycloak.models.cache.CacheUserProvider;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedUser;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.*;
|
||||
|
|
|
@ -15,9 +15,9 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache;
|
||||
package org.keycloak.models.cache.infinispan;
|
||||
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedUser;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
33
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/AbstractRevisioned.java
vendored
Executable file
33
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/AbstractRevisioned.java
vendored
Executable file
|
@ -0,0 +1,33 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class AbstractRevisioned implements Revisioned, Serializable {
|
||||
private String id;
|
||||
private Long revision;
|
||||
|
||||
public AbstractRevisioned(Long revision, String id) {
|
||||
this.revision = revision;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevision(Long revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
}
|
|
@ -15,14 +15,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.entities;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.infinispan.RealmCache;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
|
@ -37,9 +37,7 @@ import java.util.TreeMap;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedClient implements Serializable {
|
||||
|
||||
protected String id;
|
||||
public class CachedClient extends AbstractRevisioned implements InRealm {
|
||||
protected String clientId;
|
||||
protected String name;
|
||||
protected String description;
|
||||
|
@ -69,7 +67,6 @@ public class CachedClient implements Serializable {
|
|||
protected boolean implicitFlowEnabled;
|
||||
protected boolean directAccessGrantsEnabled;
|
||||
protected boolean serviceAccountsEnabled;
|
||||
protected Map<String, String> roles = new HashMap<String, String>();
|
||||
protected int nodeReRegistrationTimeout;
|
||||
protected Map<String, Integer> registeredNodes;
|
||||
protected String clientTemplate;
|
||||
|
@ -77,8 +74,8 @@ public class CachedClient implements Serializable {
|
|||
protected boolean useTemplateConfig;
|
||||
protected boolean useTemplateMappers;
|
||||
|
||||
public CachedClient(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
|
||||
id = model.getId();
|
||||
public CachedClient(Long revision, RealmModel realm, ClientModel model) {
|
||||
super(revision, model.getId());
|
||||
clientAuthenticatorType = model.getClientAuthenticatorType();
|
||||
secret = model.getSecret();
|
||||
registrationToken = model.getRegistrationToken();
|
||||
|
@ -112,10 +109,9 @@ public class CachedClient implements Serializable {
|
|||
implicitFlowEnabled = model.isImplicitFlowEnabled();
|
||||
directAccessGrantsEnabled = model.isDirectAccessGrantsEnabled();
|
||||
serviceAccountsEnabled = model.isServiceAccountsEnabled();
|
||||
cacheRoles(cache, realm, model);
|
||||
|
||||
nodeReRegistrationTimeout = model.getNodeReRegistrationTimeout();
|
||||
registeredNodes = new TreeMap<String, Integer>(model.getRegisteredNodes());
|
||||
registeredNodes = new TreeMap<>(model.getRegisteredNodes());
|
||||
if (model.getClientTemplate() != null) {
|
||||
clientTemplate = model.getClientTemplate().getId();
|
||||
}
|
||||
|
@ -124,17 +120,6 @@ public class CachedClient implements Serializable {
|
|||
useTemplateScope = model.useTemplateScope();
|
||||
}
|
||||
|
||||
protected void cacheRoles(RealmCache cache, RealmModel realm, ClientModel model) {
|
||||
for (RoleModel role : model.getRoles()) {
|
||||
roles.put(role.getName(), role.getId());
|
||||
cache.addRole(new CachedClientRole(id, role, realm));
|
||||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
|
@ -251,10 +236,6 @@ public class CachedClient implements Serializable {
|
|||
return serviceAccountsEnabled;
|
||||
}
|
||||
|
||||
public Map<String, String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public int getNodeReRegistrationTimeout() {
|
||||
return nodeReRegistrationTimeout;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.entities;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -24,17 +24,17 @@ import org.keycloak.models.RoleModel;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedClientRole extends CachedRole {
|
||||
public class CachedClientRole extends CachedRole implements InClient {
|
||||
|
||||
private final String idClient;
|
||||
private final String clientId;
|
||||
|
||||
public CachedClientRole(String idClient, RoleModel model, RealmModel realm) {
|
||||
super(model, realm);
|
||||
this.idClient = idClient;
|
||||
public CachedClientRole(Long revision, String clientId, RoleModel model, RealmModel realm) {
|
||||
super(revision, model, realm);
|
||||
this.clientId = clientId;
|
||||
|
||||
}
|
||||
|
||||
public String getIdClient() {
|
||||
return idClient;
|
||||
public String getClientId() {
|
||||
return clientId;
|
||||
}
|
||||
}
|
|
@ -15,14 +15,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.entities;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.ProtocolMapperModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.infinispan.RealmCache;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
|
@ -34,9 +34,8 @@ import java.util.Set;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedClientTemplate implements Serializable {
|
||||
public class CachedClientTemplate extends AbstractRevisioned implements InRealm {
|
||||
|
||||
private String id;
|
||||
private String name;
|
||||
private String description;
|
||||
private String realm;
|
||||
|
@ -50,12 +49,12 @@ public class CachedClientTemplate implements Serializable {
|
|||
private boolean implicitFlowEnabled;
|
||||
private boolean directAccessGrantsEnabled;
|
||||
private boolean serviceAccountsEnabled;
|
||||
private Set<String> scope = new HashSet<String>();
|
||||
private Set<String> scope = new HashSet<>();
|
||||
private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>();
|
||||
private Map<String, String> attributes = new HashMap<String, String>();
|
||||
private Map<String, String> attributes = new HashMap<>();
|
||||
|
||||
public CachedClientTemplate(RealmCache cache, RealmProvider delegate, RealmModel realm, ClientTemplateModel model) {
|
||||
id = model.getId();
|
||||
public CachedClientTemplate(Long revision, RealmModel realm, ClientTemplateModel model) {
|
||||
super(revision, model.getId());
|
||||
name = model.getName();
|
||||
description = model.getDescription();
|
||||
this.realm = realm.getId();
|
||||
|
@ -77,10 +76,6 @@ public class CachedClientTemplate implements Serializable {
|
|||
directAccessGrantsEnabled = model.isDirectAccessGrantsEnabled();
|
||||
serviceAccountsEnabled = model.isServiceAccountsEnabled();
|
||||
}
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.entities;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.models.GroupModel;
|
||||
|
@ -30,8 +30,7 @@ import java.util.Set;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedGroup implements Serializable {
|
||||
private String id;
|
||||
public class CachedGroup extends AbstractRevisioned implements InRealm {
|
||||
private String realm;
|
||||
private String name;
|
||||
private String parentId;
|
||||
|
@ -39,8 +38,8 @@ public class CachedGroup implements Serializable {
|
|||
private Set<String> roleMappings = new HashSet<>();
|
||||
private Set<String> subGroups = new HashSet<>();
|
||||
|
||||
public CachedGroup(RealmModel realm, GroupModel group) {
|
||||
this.id = group.getId();
|
||||
public CachedGroup(Long revision, RealmModel realm, GroupModel group) {
|
||||
super(revision, group.getId());
|
||||
this.realm = realm.getId();
|
||||
this.name = group.getName();
|
||||
this.parentId = group.getParentId();
|
||||
|
@ -57,10 +56,6 @@ public class CachedGroup implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.entities;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
|
@ -35,7 +35,7 @@ import org.keycloak.models.RequiredCredentialModel;
|
|||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserFederationMapperModel;
|
||||
import org.keycloak.models.UserFederationProviderModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.infinispan.RealmCache;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -56,9 +56,8 @@ import java.util.Set;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedRealm implements Serializable {
|
||||
public class CachedRealm extends AbstractRevisioned {
|
||||
|
||||
protected String id;
|
||||
protected String name;
|
||||
protected String displayName;
|
||||
protected String displayNameHtml;
|
||||
|
@ -146,8 +145,6 @@ public class CachedRealm implements Serializable {
|
|||
|
||||
protected List<String> defaultGroups = new LinkedList<String>();
|
||||
protected Set<String> groups = new HashSet<String>();
|
||||
protected Map<String, String> realmRoles = new HashMap<String, String>();
|
||||
protected List<String> clients = new LinkedList<>();
|
||||
protected List<String> clientTemplates= new LinkedList<>();
|
||||
protected boolean internationalizationEnabled;
|
||||
protected Set<String> supportedLocales;
|
||||
|
@ -155,11 +152,8 @@ public class CachedRealm implements Serializable {
|
|||
protected MultivaluedHashMap<String, IdentityProviderMapperModel> identityProviderMappers = new MultivaluedHashMap<>();
|
||||
protected Set<IdentityProviderMapperModel> identityProviderMapperSet;
|
||||
|
||||
public CachedRealm() {
|
||||
}
|
||||
|
||||
public CachedRealm(RealmCache cache, RealmProvider delegate, RealmModel model) {
|
||||
id = model.getId();
|
||||
public CachedRealm(Long revision, RealmModel model) {
|
||||
super(revision, model.getId());
|
||||
name = model.getName();
|
||||
displayName = model.getDisplayName();
|
||||
displayNameHtml = model.getDisplayNameHtml();
|
||||
|
@ -244,11 +238,7 @@ public class CachedRealm implements Serializable {
|
|||
ClientModel masterAdminClient = model.getMasterAdminClient();
|
||||
this.masterAdminClient = (masterAdminClient != null) ? masterAdminClient.getId() : null;
|
||||
|
||||
cacheRealmRoles(cache, model);
|
||||
|
||||
cacheClients(cache, delegate, model);
|
||||
|
||||
cacheClientTemplates(cache, delegate, model);
|
||||
cacheClientTemplates(model);
|
||||
|
||||
internationalizationEnabled = model.isInternationalizationEnabled();
|
||||
supportedLocales = model.getSupportedLocales();
|
||||
|
@ -286,35 +276,12 @@ public class CachedRealm implements Serializable {
|
|||
|
||||
}
|
||||
|
||||
protected void cacheClientTemplates(RealmCache cache, RealmProvider delegate, RealmModel model) {
|
||||
protected void cacheClientTemplates(RealmModel model) {
|
||||
for (ClientTemplateModel template : model.getClientTemplates()) {
|
||||
clientTemplates.add(template.getId());
|
||||
CachedClientTemplate cachedClient = new CachedClientTemplate(cache, delegate, model, template);
|
||||
cache.addClientTemplate(cachedClient);
|
||||
}
|
||||
}
|
||||
|
||||
protected void cacheClients(RealmCache cache, RealmProvider delegate, RealmModel model) {
|
||||
for (ClientModel client : model.getClients()) {
|
||||
clients.add(client.getId());
|
||||
CachedClient cachedClient = new CachedClient(cache, delegate, model, client);
|
||||
cache.addClient(cachedClient);
|
||||
}
|
||||
}
|
||||
|
||||
protected void cacheRealmRoles(RealmCache cache, RealmModel model) {
|
||||
for (RoleModel role : model.getRoles()) {
|
||||
realmRoles.put(role.getName(), role.getId());
|
||||
CachedRole cachedRole = new CachedRealmRole(role, model);
|
||||
cache.addRole(cachedRole);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getMasterAdminClient() {
|
||||
return masterAdminClient;
|
||||
}
|
||||
|
@ -335,14 +302,6 @@ public class CachedRealm implements Serializable {
|
|||
return defaultRoles;
|
||||
}
|
||||
|
||||
public Map<String, String> getRealmRoles() {
|
||||
return realmRoles;
|
||||
}
|
||||
|
||||
public List<String> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.entities;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -27,8 +27,8 @@ import org.keycloak.models.RoleModel;
|
|||
public class CachedRealmRole extends CachedRole {
|
||||
|
||||
|
||||
public CachedRealmRole(RoleModel model, RealmModel realm) {
|
||||
super(model, realm);
|
||||
public CachedRealmRole(Long revision, RoleModel model, RealmModel realm) {
|
||||
super(revision, model, realm);
|
||||
|
||||
}
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.entities;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -28,9 +28,8 @@ import java.util.Set;
|
|||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class CachedRole implements Serializable {
|
||||
public class CachedRole extends AbstractRevisioned implements InRealm {
|
||||
|
||||
final protected String id;
|
||||
final protected String name;
|
||||
final protected String realm;
|
||||
final protected String description;
|
||||
|
@ -38,10 +37,10 @@ public class CachedRole implements Serializable {
|
|||
final protected boolean composite;
|
||||
final protected Set<String> composites = new HashSet<String>();
|
||||
|
||||
public CachedRole(RoleModel model, RealmModel realm) {
|
||||
public CachedRole(Long revision, RoleModel model, RealmModel realm) {
|
||||
super(revision, model.getId());
|
||||
composite = model.isComposite();
|
||||
description = model.getDescription();
|
||||
id = model.getId();
|
||||
name = model.getName();
|
||||
scopeParamRequired = model.isScopeParamRequired();
|
||||
this.realm = realm.getId();
|
||||
|
@ -53,10 +52,6 @@ public class CachedRole implements Serializable {
|
|||
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
|
@ -15,7 +15,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.models.cache.entities;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.RealmModel;
|
49
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/ClientListQuery.java
vendored
Executable file
49
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/ClientListQuery.java
vendored
Executable file
|
@ -0,0 +1,49 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientListQuery extends AbstractRevisioned implements ClientQuery {
|
||||
private final Set<String> clients;
|
||||
private final String realm;
|
||||
private final String realmName;
|
||||
|
||||
public ClientListQuery(Long revisioned, String id, RealmModel realm, Set<String> clients) {
|
||||
super(revisioned, id);
|
||||
this.realm = realm.getId();
|
||||
this.realmName = realm.getName();
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
public ClientListQuery(Long revisioned, String id, RealmModel realm, String client) {
|
||||
super(revisioned, id);
|
||||
this.realm = realm.getId();
|
||||
this.realmName = realm.getName();
|
||||
this.clients = new HashSet<>();
|
||||
this.clients.add(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClientListQuery{" +
|
||||
"id='" + getId() + "'" +
|
||||
"realmName='" + realmName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
13
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/ClientQuery.java
vendored
Executable file
13
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/ClientQuery.java
vendored
Executable file
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.InRealm;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ClientQuery extends InRealm {
|
||||
Set<String> getClients();
|
||||
}
|
13
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/ClientTemplateQuery.java
vendored
Executable file
13
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/ClientTemplateQuery.java
vendored
Executable file
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.InRealm;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface ClientTemplateQuery extends InRealm {
|
||||
Set<String> getTemplates();
|
||||
}
|
13
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/GroupQuery.java
vendored
Executable file
13
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/GroupQuery.java
vendored
Executable file
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.InRealm;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface GroupQuery extends InRealm {
|
||||
Set<String> getGroups();
|
||||
}
|
9
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/InClient.java
vendored
Executable file
9
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/InClient.java
vendored
Executable file
|
@ -0,0 +1,9 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface InClient extends InRealm {
|
||||
String getClientId();
|
||||
}
|
9
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/InRealm.java
vendored
Executable file
9
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/InRealm.java
vendored
Executable file
|
@ -0,0 +1,9 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface InRealm extends Revisioned {
|
||||
String getRealm();
|
||||
}
|
27
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RealmListQuery.java
vendored
Executable file
27
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RealmListQuery.java
vendored
Executable file
|
@ -0,0 +1,27 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RealmListQuery extends AbstractRevisioned implements RealmQuery {
|
||||
private final Set<String> realms;
|
||||
|
||||
public RealmListQuery(Long revision, String id, String realm) {
|
||||
super(revision, id);
|
||||
realms = new HashSet<>();
|
||||
realms.add(realm);
|
||||
}
|
||||
public RealmListQuery(Long revision, String id, Set<String> realms) {
|
||||
super(revision, id);
|
||||
this.realms = realms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRealms() {
|
||||
return realms;
|
||||
}
|
||||
}
|
11
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RealmQuery.java
vendored
Executable file
11
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RealmQuery.java
vendored
Executable file
|
@ -0,0 +1,11 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface RealmQuery {
|
||||
Set<String> getRealms();
|
||||
}
|
|
@ -1,10 +1,11 @@
|
|||
package org.keycloak.models.cache.infinispan.locking;
|
||||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface Revisioned {
|
||||
String getId();
|
||||
Long getRevision();
|
||||
void setRevision(Long revision);
|
||||
}
|
68
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RoleListQuery.java
vendored
Executable file
68
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RoleListQuery.java
vendored
Executable file
|
@ -0,0 +1,68 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.cache.infinispan.entities.AbstractRevisioned;
|
||||
import org.keycloak.models.cache.infinispan.entities.ClientQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.RoleQuery;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RoleListQuery extends AbstractRevisioned implements RoleQuery, InClient {
|
||||
private final Set<String> roles;
|
||||
private final String realm;
|
||||
private final String realmName;
|
||||
private String client;
|
||||
|
||||
public RoleListQuery(Long revisioned, String id, RealmModel realm, Set<String> roles) {
|
||||
super(revisioned, id);
|
||||
this.realm = realm.getId();
|
||||
this.realmName = realm.getName();
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public RoleListQuery(Long revisioned, String id, RealmModel realm, String role) {
|
||||
super(revisioned, id);
|
||||
this.realm = realm.getId();
|
||||
this.realmName = realm.getName();
|
||||
this.roles = new HashSet<>();
|
||||
this.roles.add(role);
|
||||
}
|
||||
|
||||
public RoleListQuery(Long revision, String id, RealmModel realm, Set<String> roles, String client) {
|
||||
this(revision, id, realm, roles);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public RoleListQuery(Long revision, String id, RealmModel realm, String role, String client) {
|
||||
this(revision, id, realm, role);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRealm() {
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClientId() {
|
||||
return client;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RoleListQuery{" +
|
||||
"id='" + getId() + "'" +
|
||||
"realmName='" + realmName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
13
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RoleQuery.java
vendored
Executable file
13
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/RoleQuery.java
vendored
Executable file
|
@ -0,0 +1,13 @@
|
|||
package org.keycloak.models.cache.infinispan.entities;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.InRealm;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface RoleQuery extends InRealm {
|
||||
Set<String> getRoles();
|
||||
}
|
|
@ -1,523 +0,0 @@
|
|||
/*
|
||||
* 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.locking;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakTransaction;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.ClientAdapter;
|
||||
import org.keycloak.models.cache.infinispan.ClientTemplateAdapter;
|
||||
import org.keycloak.models.cache.infinispan.GroupAdapter;
|
||||
import org.keycloak.models.cache.infinispan.RealmAdapter;
|
||||
import org.keycloak.models.cache.infinispan.RoleAdapter;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClient;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.locking.entities.RevisionedCachedRealmRole;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
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 LockingCacheRealmProvider implements CacheRealmProvider {
|
||||
protected static final Logger logger = Logger.getLogger(LockingCacheRealmProvider.class);
|
||||
protected LockingRealmCache cache;
|
||||
protected KeycloakSession session;
|
||||
protected RealmProvider delegate;
|
||||
protected boolean transactionActive;
|
||||
protected boolean setRollbackOnly;
|
||||
|
||||
protected Set<String> realmInvalidations = new HashSet<>();
|
||||
protected Set<String> appInvalidations = new HashSet<>();
|
||||
protected Set<String> clientTemplateInvalidations = new HashSet<>();
|
||||
protected Set<String> roleInvalidations = new HashSet<>();
|
||||
protected Set<String> groupInvalidations = new HashSet<>();
|
||||
protected Map<String, RealmModel> managedRealms = new HashMap<>();
|
||||
protected Map<String, ClientModel> managedApplications = new HashMap<>();
|
||||
protected Map<String, ClientTemplateModel> managedClientTemplates = new HashMap<>();
|
||||
protected Map<String, RoleModel> managedRoles = new HashMap<>();
|
||||
protected Map<String, GroupModel> managedGroups = new HashMap<>();
|
||||
|
||||
protected boolean clearAll;
|
||||
|
||||
public LockingCacheRealmProvider(LockingRealmCache cache, KeycloakSession session) {
|
||||
this.cache = cache;
|
||||
this.session = session;
|
||||
|
||||
session.getTransaction().enlistPrepare(getPrepareTransaction());
|
||||
session.getTransaction().enlistAfterCompletion(getAfterTransaction());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MigrationModel getMigrationModel() {
|
||||
return getDelegate().getMigrationModel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmProvider getDelegate() {
|
||||
if (!transactionActive) throw new IllegalStateException("Cannot access delegate without a transaction");
|
||||
if (delegate != null) return delegate;
|
||||
delegate = session.getProvider(RealmProvider.class);
|
||||
return delegate;
|
||||
}
|
||||
|
||||
public LockingRealmCache getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRealmInvalidation(String id) {
|
||||
realmInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerApplicationInvalidation(String id) {
|
||||
appInvalidations.add(id);
|
||||
}
|
||||
@Override
|
||||
public void registerClientTemplateInvalidation(String id) {
|
||||
clientTemplateInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerRoleInvalidation(String id) {
|
||||
roleInvalidations.add(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerGroupInvalidation(String id) {
|
||||
groupInvalidations.add(id);
|
||||
|
||||
}
|
||||
|
||||
protected void runInvalidations() {
|
||||
for (String id : realmInvalidations) {
|
||||
cache.invalidateRealmById(id);
|
||||
}
|
||||
for (String id : roleInvalidations) {
|
||||
cache.invalidateRoleById(id);
|
||||
}
|
||||
for (String id : groupInvalidations) {
|
||||
cache.invalidateGroupById(id);
|
||||
}
|
||||
for (String id : appInvalidations) {
|
||||
cache.invalidateClientById(id);
|
||||
}
|
||||
for (String id : clientTemplateInvalidations) {
|
||||
cache.invalidateClientTemplateById(id);
|
||||
}
|
||||
}
|
||||
|
||||
private KeycloakTransaction getPrepareTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
transactionActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
if (delegate == null) return;
|
||||
List<String> invalidates = new LinkedList<>();
|
||||
for (String id : realmInvalidations) {
|
||||
invalidates.add(id);
|
||||
}
|
||||
for (String id : roleInvalidations) {
|
||||
invalidates.add(id);
|
||||
}
|
||||
for (String id : groupInvalidations) {
|
||||
invalidates.add(id);
|
||||
}
|
||||
for (String id : appInvalidations) {
|
||||
invalidates.add(id);
|
||||
}
|
||||
for (String id : clientTemplateInvalidations) {
|
||||
invalidates.add(id);
|
||||
}
|
||||
|
||||
Collections.sort(invalidates); // lock ordering
|
||||
cache.getRevisions().startBatch();
|
||||
for (String id : invalidates) {
|
||||
cache.getRevisions().getAdvancedCache().lock(id);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
setRollbackOnly = true;
|
||||
transactionActive = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return setRollbackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transactionActive;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private KeycloakTransaction getAfterTransaction() {
|
||||
return new KeycloakTransaction() {
|
||||
@Override
|
||||
public void begin() {
|
||||
transactionActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void commit() {
|
||||
try {
|
||||
if (delegate == null) return;
|
||||
if (clearAll) {
|
||||
cache.clear();
|
||||
}
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
} finally {
|
||||
cache.endRevisionBatch();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
try {
|
||||
setRollbackOnly = true;
|
||||
runInvalidations();
|
||||
transactionActive = false;
|
||||
} finally {
|
||||
cache.endRevisionBatch();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRollbackOnly() {
|
||||
setRollbackOnly = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getRollbackOnly() {
|
||||
return setRollbackOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return transactionActive;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String name) {
|
||||
RealmModel realm = getDelegate().createRealm(name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel createRealm(String id, String name) {
|
||||
RealmModel realm = getDelegate().createRealm(id, name);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
return realm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealm(String id) {
|
||||
CachedRealm cached = cache.getRealm(id);
|
||||
if (cached != null) {
|
||||
logger.tracev("by id cache hit: {0}", cached.getName());
|
||||
}
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
RealmModel model = getDelegate().getRealm(id);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(id)) return model;
|
||||
cached = new RevisionedCachedRealm(loaded, cache, this, model);
|
||||
cache.addRealm(cached);
|
||||
} else if (realmInvalidations.contains(id)) {
|
||||
return getDelegate().getRealm(id);
|
||||
} else if (managedRealms.containsKey(id)) {
|
||||
return managedRealms.get(id);
|
||||
}
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealmByName(String name) {
|
||||
CachedRealm cached = cache.getRealmByName(name);
|
||||
if (cached != null) {
|
||||
logger.tracev("by name cache hit: {0}", cached.getName());
|
||||
}
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
RealmModel model = getDelegate().getRealmByName(name);
|
||||
if (model == null) return null;
|
||||
if (realmInvalidations.contains(model.getId())) return model;
|
||||
cached = new RevisionedCachedRealm(loaded, cache, this, model);
|
||||
cache.addRealm(cached);
|
||||
} else if (realmInvalidations.contains(cached.getId())) {
|
||||
return getDelegate().getRealmByName(name);
|
||||
} else if (managedRealms.containsKey(cached.getId())) {
|
||||
return managedRealms.get(cached.getId());
|
||||
}
|
||||
RealmAdapter adapter = new RealmAdapter(cached, this);
|
||||
managedRealms.put(cached.getId(), adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RealmModel> getRealms() {
|
||||
// Retrieve realms from backend
|
||||
List<RealmModel> backendRealms = getDelegate().getRealms();
|
||||
|
||||
// Return cache delegates to ensure cache invalidated during write operations
|
||||
List<RealmModel> cachedRealms = new LinkedList<RealmModel>();
|
||||
for (RealmModel realm : backendRealms) {
|
||||
RealmModel cached = getRealm(realm.getId());
|
||||
cachedRealms.add(cached);
|
||||
}
|
||||
return cachedRealms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRealm(String id) {
|
||||
cache.invalidateRealmById(id);
|
||||
|
||||
RealmModel realm = getDelegate().getRealm(id);
|
||||
Set<RoleModel> realmRoles = null;
|
||||
if (realm != null) {
|
||||
realmRoles = realm.getRoles();
|
||||
}
|
||||
|
||||
boolean didIt = getDelegate().removeRealm(id);
|
||||
realmInvalidations.add(id);
|
||||
|
||||
// TODO: Temporary workaround to invalidate cached realm roles
|
||||
if (didIt && realmRoles != null) {
|
||||
for (RoleModel role : realmRoles) {
|
||||
roleInvalidations.add(role.getId());
|
||||
}
|
||||
}
|
||||
|
||||
return didIt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeClient(String id, RealmModel realm) {
|
||||
ClientModel client = getClientById(id, realm);
|
||||
if (client == null) return false;
|
||||
|
||||
registerApplicationInvalidation(id);
|
||||
registerRealmInvalidation(realm.getId());
|
||||
cache.invalidateClientById(id);
|
||||
cache.invalidateRealmById(realm.getId());
|
||||
|
||||
|
||||
|
||||
Set<RoleModel> roles = client.getRoles();
|
||||
for (RoleModel role : roles) {
|
||||
registerRoleInvalidation(role.getId());
|
||||
}
|
||||
return getDelegate().removeClient(id, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (delegate != null) delegate.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id, RealmModel realm) {
|
||||
CachedRole cached = cache.getRole(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
RoleModel model = getDelegate().getRoleById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (roleInvalidations.contains(id)) return model;
|
||||
if (model.getContainer() instanceof ClientModel) {
|
||||
cached = new RevisionedCachedClientRole(loaded, ((ClientModel) model.getContainer()).getId(), model, realm);
|
||||
} else {
|
||||
cached = new RevisionedCachedRealmRole(loaded, model, realm);
|
||||
}
|
||||
cache.addRole(cached);
|
||||
|
||||
} else if (roleInvalidations.contains(id)) {
|
||||
return getDelegate().getRoleById(id, realm);
|
||||
} else if (managedRoles.containsKey(id)) {
|
||||
return managedRoles.get(id);
|
||||
}
|
||||
RoleAdapter adapter = new RoleAdapter(cached, cache, this, realm);
|
||||
managedRoles.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupModel getGroupById(String id, RealmModel realm) {
|
||||
CachedGroup cached = cache.getGroup(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
GroupModel model = getDelegate().getGroupById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (groupInvalidations.contains(id)) return model;
|
||||
cached = new RevisionedCachedGroup(loaded, realm, model);
|
||||
cache.addGroup(cached);
|
||||
|
||||
} else if (groupInvalidations.contains(id)) {
|
||||
return getDelegate().getGroupById(id, realm);
|
||||
} else if (managedGroups.containsKey(id)) {
|
||||
return managedGroups.get(id);
|
||||
}
|
||||
GroupAdapter adapter = new GroupAdapter(cached, this, session, realm);
|
||||
managedGroups.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
CachedClient cached = cache.getClient(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
if (cached != null) {
|
||||
logger.tracev("client by id cache hit: {0}", cached.getClientId());
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
ClientModel model = getDelegate().getClientById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (appInvalidations.contains(id)) return model;
|
||||
cached = new RevisionedCachedClient(loaded, cache, getDelegate(), realm, model);
|
||||
logger.tracev("adding client by id cache miss: {0}", cached.getClientId());
|
||||
cache.addClient(cached);
|
||||
} else if (appInvalidations.contains(id)) {
|
||||
return getDelegate().getClientById(id, realm);
|
||||
} else if (managedApplications.containsKey(id)) {
|
||||
return managedApplications.get(id);
|
||||
}
|
||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache);
|
||||
managedApplications.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||
CachedClient cached = cache.getClientByClientId(realm, clientId);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
if (cached != null) {
|
||||
logger.tracev("client by name cache hit: {0}", cached.getClientId());
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = UpdateCounter.current();
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
ClientModel model = getDelegate().getClientByClientId(clientId, realm);
|
||||
if (model == null) return null;
|
||||
if (appInvalidations.contains(model.getId())) return model;
|
||||
cached = new RevisionedCachedClient(loaded, cache, getDelegate(), realm, model);
|
||||
logger.tracev("adding client by name cache miss: {0}", cached.getClientId());
|
||||
cache.addClient(cached);
|
||||
} else if (appInvalidations.contains(cached.getId())) {
|
||||
return getDelegate().getClientById(cached.getId(), realm);
|
||||
} else if (managedApplications.containsKey(cached.getId())) {
|
||||
return managedApplications.get(cached.getId());
|
||||
}
|
||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this, cache);
|
||||
managedApplications.put(cached.getId(), adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientTemplateModel getClientTemplateById(String id, RealmModel realm) {
|
||||
CachedClientTemplate cached = cache.getClientTemplate(id);
|
||||
if (cached != null && !cached.getRealm().equals(realm.getId())) {
|
||||
cached = null;
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
Long loaded = cache.getCurrentRevision(id);
|
||||
if (loaded == null) loaded = UpdateCounter.current();
|
||||
ClientTemplateModel model = getDelegate().getClientTemplateById(id, realm);
|
||||
if (model == null) return null;
|
||||
if (clientTemplateInvalidations.contains(id)) return model;
|
||||
cached = new RevisionedCachedClientTemplate(loaded, cache, getDelegate(), realm, model);
|
||||
cache.addClientTemplate(cached);
|
||||
} else if (clientTemplateInvalidations.contains(id)) {
|
||||
return getDelegate().getClientTemplateById(id, realm);
|
||||
} else if (managedClientTemplates.containsKey(id)) {
|
||||
return managedClientTemplates.get(id);
|
||||
}
|
||||
ClientTemplateModel adapter = new ClientTemplateAdapter(realm, cached, this, cache);
|
||||
managedClientTemplates.put(id, adapter);
|
||||
return adapter;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,159 +0,0 @@
|
|||
/*
|
||||
* 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.locking;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.notifications.Listener;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.cache.CacheRealmProvider;
|
||||
import org.keycloak.models.cache.CacheRealmProviderFactory;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class LockingCacheRealmProviderFactory implements CacheRealmProviderFactory {
|
||||
|
||||
private static final Logger log = Logger.getLogger(LockingCacheRealmProviderFactory.class);
|
||||
|
||||
protected volatile LockingRealmCache realmCache;
|
||||
|
||||
@Override
|
||||
public CacheRealmProvider create(KeycloakSession session) {
|
||||
lazyInit(session);
|
||||
return new LockingCacheRealmProvider(realmCache, session);
|
||||
}
|
||||
|
||||
private void lazyInit(KeycloakSession session) {
|
||||
if (realmCache == null) {
|
||||
synchronized (this) {
|
||||
if (realmCache == null) {
|
||||
Cache<String, Object> cache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
|
||||
Cache<String, Long> counterCache = session.getProvider(InfinispanConnectionProvider.class).getCache(LockingConnectionProviderFactory.VERSION_CACHE_NAME);
|
||||
cache.addListener(new CacheListener());
|
||||
realmCache = new LockingRealmCache(cache, counterCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "infinispan-locking";
|
||||
}
|
||||
|
||||
@Listener
|
||||
public class CacheListener {
|
||||
|
||||
@CacheEntryCreated
|
||||
public void created(CacheEntryCreatedEvent<String, Object> event) {
|
||||
if (!event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm realm = (CachedRealm) object;
|
||||
realmCache.getRealmLookup().put(realm.getName(), realm.getId());
|
||||
log.tracev("Realm added realm={0}", realm.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryRemoved
|
||||
public void removed(CacheEntryRemovedEvent<String, Object> event) {
|
||||
if (event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntryInvalidated
|
||||
public void removed(CacheEntryInvalidatedEvent<String, Object> event) {
|
||||
if (event.isPre()) {
|
||||
Object object = event.getValue();
|
||||
if (object != null) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CacheEntriesEvicted
|
||||
public void userEvicted(CacheEntriesEvictedEvent<String, Object> event) {
|
||||
for (Object object : event.getEntries().values()) {
|
||||
remove(object);
|
||||
}
|
||||
}
|
||||
|
||||
private void remove(Object object) {
|
||||
if (object instanceof CachedRealm) {
|
||||
CachedRealm realm = (CachedRealm) object;
|
||||
|
||||
realmCache.getRealmLookup().remove(realm.getName());
|
||||
|
||||
for (String r : realm.getRealmRoles().values()) {
|
||||
realmCache.evictRoleById(r);
|
||||
}
|
||||
|
||||
for (String c : realm.getClients()) {
|
||||
realmCache.evictClientById(c);
|
||||
}
|
||||
|
||||
log.tracev("Realm removed realm={0}", realm.getName());
|
||||
} else if (object instanceof CachedClient) {
|
||||
CachedClient client = (CachedClient) object;
|
||||
|
||||
realmCache.getClientLookup().remove(client.getRealm() + "." + client.getClientId());
|
||||
|
||||
for (String r : client.getRoles().values()) {
|
||||
realmCache.evictRoleById(r);
|
||||
}
|
||||
|
||||
log.tracev("Client removed client={0}", client.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
/*
|
||||
* 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.locking;
|
||||
|
||||
import org.infinispan.configuration.cache.Configuration;
|
||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||
import org.infinispan.transaction.LockingMode;
|
||||
import org.infinispan.transaction.TransactionMode;
|
||||
import org.infinispan.transaction.lookup.DummyTransactionManagerLookup;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class LockingConnectionProviderFactory extends DefaultInfinispanConnectionProviderFactory {
|
||||
public static final String VERSION_CACHE_NAME = "realmVersions";
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(LockingConnectionProviderFactory.class);
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "locking";
|
||||
}
|
||||
|
||||
|
||||
protected void initEmbedded() {
|
||||
super.initEmbedded();
|
||||
ConfigurationBuilder counterConfigBuilder = new ConfigurationBuilder();
|
||||
counterConfigBuilder.invocationBatching().enable()
|
||||
.transaction().transactionMode(TransactionMode.TRANSACTIONAL);
|
||||
counterConfigBuilder.transaction().transactionManagerLookup(new DummyTransactionManagerLookup());
|
||||
counterConfigBuilder.transaction().lockingMode(LockingMode.PESSIMISTIC);
|
||||
Configuration counterCacheConfiguration = counterConfigBuilder.build();
|
||||
|
||||
cacheManager.defineConfiguration(VERSION_CACHE_NAME, counterCacheConfiguration);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,300 +0,0 @@
|
|||
/*
|
||||
* 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.locking;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.entities.CachedRole;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class LockingRealmCache implements RealmCache {
|
||||
|
||||
protected static final Logger logger = Logger.getLogger(LockingRealmCache.class);
|
||||
|
||||
protected final Cache<String, Long> revisions;
|
||||
protected final Cache<String, Object> cache;
|
||||
|
||||
protected final ConcurrentHashMap<String, String> realmLookup = new ConcurrentHashMap<>();
|
||||
protected final ConcurrentHashMap<String, String> clientLookup = new ConcurrentHashMap<>();
|
||||
|
||||
public LockingRealmCache(Cache<String, Object> cache, Cache<String, Long> revisions) {
|
||||
this.cache = cache;
|
||||
this.revisions = revisions;
|
||||
}
|
||||
|
||||
public Cache<String, Object> getCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public Cache<String, Long> getRevisions() {
|
||||
return revisions;
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, String> getRealmLookup() {
|
||||
return realmLookup;
|
||||
}
|
||||
|
||||
public ConcurrentHashMap<String, String> getClientLookup() {
|
||||
return clientLookup;
|
||||
}
|
||||
|
||||
public Long getCurrentRevision(String id) {
|
||||
return revisions.get(id);
|
||||
}
|
||||
|
||||
public void endRevisionBatch() {
|
||||
try {
|
||||
revisions.endBatch(true);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private <T> T get(String id, Class<T> type) {
|
||||
Revisioned o = (Revisioned)cache.get(id);
|
||||
if (o == null) {
|
||||
return null;
|
||||
}
|
||||
Long rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
logger.tracev("get() missing rev");
|
||||
return null;
|
||||
}
|
||||
long oRev = o.getRevision() == null ? -1L : o.getRevision().longValue();
|
||||
if (rev > oRev) {
|
||||
logger.tracev("get() rev: {0} o.rev: {1}", rev.longValue(), oRev);
|
||||
return null;
|
||||
}
|
||||
return o != null && type.isInstance(o) ? type.cast(o) : null;
|
||||
}
|
||||
|
||||
protected Object invalidateObject(String id) {
|
||||
Object removed = cache.remove(id);
|
||||
revisions.put(id, UpdateCounter.next());
|
||||
return removed;
|
||||
}
|
||||
|
||||
protected void addRevisioned(String id, Revisioned object) {
|
||||
//startRevisionBatch();
|
||||
try {
|
||||
//revisions.getAdvancedCache().lock(id);
|
||||
Long rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
rev = UpdateCounter.current();
|
||||
revisions.put(id, rev);
|
||||
}
|
||||
revisions.startBatch();
|
||||
if (!revisions.getAdvancedCache().lock(id)) {
|
||||
logger.trace("Could not obtain version lock");
|
||||
}
|
||||
rev = revisions.get(id);
|
||||
if (rev == null) {
|
||||
return;
|
||||
}
|
||||
if (rev.equals(object.getRevision())) {
|
||||
cache.putForExternalRead(id, object);
|
||||
return;
|
||||
}
|
||||
if (rev > object.getRevision()) { // revision is ahead, don't cache
|
||||
return;
|
||||
}
|
||||
// revisions cache has a lower value than the object.revision, so update revision and add it to cache
|
||||
revisions.put(id, object.getRevision());
|
||||
cache.putForExternalRead(id, object);
|
||||
} finally {
|
||||
endRevisionBatch();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRealm getRealm(String id) {
|
||||
return get(id, CachedRealm.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRealm(CachedRealm realm) {
|
||||
logger.tracev("Invalidating realm {0}", realm.getId());
|
||||
invalidateObject(realm.getId());
|
||||
realmLookup.remove(realm.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRealmById(String id) {
|
||||
CachedRealm cached = (CachedRealm) invalidateObject(id);
|
||||
if (cached != null) realmLookup.remove(cached.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRealm(CachedRealm realm) {
|
||||
logger.tracev("Adding realm {0}", realm.getId());
|
||||
addRevisioned(realm.getId(), (Revisioned) realm);
|
||||
realmLookup.put(realm.getName(), realm.getId());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public CachedRealm getRealmByName(String name) {
|
||||
String id = realmLookup.get(name);
|
||||
return id != null ? getRealm(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedClient getClient(String id) {
|
||||
return get(id, CachedClient.class);
|
||||
}
|
||||
|
||||
public CachedClient getClientByClientId(RealmModel realm, String clientId) {
|
||||
String id = clientLookup.get(realm.getId() + "." + clientId);
|
||||
return id != null ? getClient(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClient(CachedClient app) {
|
||||
logger.tracev("Removing application {0}", app.getId());
|
||||
invalidateObject(app.getId());
|
||||
clientLookup.remove(getClientIdKey(app));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClient(CachedClient app) {
|
||||
logger.tracev("Adding application {0}", app.getId());
|
||||
addRevisioned(app.getId(), (Revisioned) app);
|
||||
clientLookup.put(getClientIdKey(app), app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClientById(String id) {
|
||||
CachedClient client = (CachedClient)invalidateObject(id);
|
||||
if (client != null) {
|
||||
logger.tracev("Removing application {0}", client.getClientId());
|
||||
clientLookup.remove(getClientIdKey(client));
|
||||
}
|
||||
}
|
||||
|
||||
protected String getClientIdKey(CachedClient client) {
|
||||
return client.getRealm() + "." + client.getClientId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictClientById(String id) {
|
||||
logger.tracev("Evicting application {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedGroup getGroup(String id) {
|
||||
return get(id, CachedGroup.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroup(CachedGroup role) {
|
||||
logger.tracev("Removing group {0}", role.getId());
|
||||
invalidateObject(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addGroup(CachedGroup role) {
|
||||
logger.tracev("Adding group {0}", role.getId());
|
||||
addRevisioned(role.getId(), (Revisioned) role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateGroupById(String id) {
|
||||
logger.tracev("Removing group {0}", id);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedRole getRole(String id) {
|
||||
return get(id, CachedRole.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRole(CachedRole role) {
|
||||
logger.tracev("Removing role {0}", role.getId());
|
||||
invalidateObject(role.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateRoleById(String id) {
|
||||
logger.tracev("Removing role {0}", id);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictRoleById(String id) {
|
||||
logger.tracev("Evicting role {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRole(CachedRole role) {
|
||||
logger.tracev("Adding role {0}", role.getId());
|
||||
addRevisioned(role.getId(), (Revisioned) role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedClientTemplate getClientTemplate(String id) {
|
||||
return get(id, CachedClientTemplate.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Removing client template {0}", app.getId());
|
||||
invalidateObject(app.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClientTemplate(CachedClientTemplate app) {
|
||||
logger.tracev("Adding client template {0}", app.getId());
|
||||
addRevisioned(app.getId(), (Revisioned) app);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateClientTemplateById(String id) {
|
||||
logger.tracev("Removing client template {0}", id);
|
||||
invalidateObject(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void evictClientTemplateById(String id) {
|
||||
logger.tracev("Evicting client template {0}", id);
|
||||
cache.evict(id);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClient;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RevisionedCachedClient extends CachedClient implements Revisioned {
|
||||
|
||||
public RevisionedCachedClient(Long revision, RealmCache cache, RealmProvider delegate, RealmModel realm, ClientModel model) {
|
||||
super(cache, delegate, realm, model);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
private Long revision;
|
||||
|
||||
@Override
|
||||
public Long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevision(Long revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cacheRoles(RealmCache cache, RealmModel realm, ClientModel model) {
|
||||
for (RoleModel role : model.getRoles()) {
|
||||
roles.put(role.getName(), role.getId());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.entities.CachedClientRole;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RevisionedCachedClientRole extends CachedClientRole implements Revisioned {
|
||||
|
||||
public RevisionedCachedClientRole(Long revision, String idClient, RoleModel model, RealmModel realm) {
|
||||
super(idClient, model, realm);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
private Long revision;
|
||||
|
||||
@Override
|
||||
public Long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevision(Long revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedClientTemplate;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RevisionedCachedClientTemplate extends CachedClientTemplate implements Revisioned {
|
||||
|
||||
public RevisionedCachedClientTemplate(Long revision, RealmCache cache, RealmProvider delegate, RealmModel realm, ClientTemplateModel model) {
|
||||
super(cache, delegate, realm, model);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
private Long revision;
|
||||
|
||||
@Override
|
||||
public Long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevision(Long revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.cache.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RevisionedCachedGroup extends CachedGroup implements Revisioned {
|
||||
public RevisionedCachedGroup(Long revision, RealmModel realm, GroupModel group) {
|
||||
super(realm, group);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
private Long revision;
|
||||
|
||||
@Override
|
||||
public Long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevision(Long revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.RealmCache;
|
||||
import org.keycloak.models.cache.entities.CachedRealm;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RevisionedCachedRealm extends CachedRealm implements Revisioned {
|
||||
|
||||
public RevisionedCachedRealm(Long revision, RealmCache cache, RealmProvider delegate, RealmModel model) {
|
||||
super(cache, delegate, model);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
private Long revision;
|
||||
|
||||
@Override
|
||||
public Long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevision(Long revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cacheClientTemplates(RealmCache cache, RealmProvider delegate, RealmModel model) {
|
||||
for (ClientTemplateModel template : model.getClientTemplates()) {
|
||||
clientTemplates.add(template.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cacheClients(RealmCache cache, RealmProvider delegate, RealmModel model) {
|
||||
for (ClientModel client : model.getClients()) {
|
||||
clients.add(client.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cacheRealmRoles(RealmCache cache, RealmModel model) {
|
||||
for (RoleModel role : model.getRoles()) {
|
||||
realmRoles.put(role.getName(), role.getId());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.cache.entities.CachedRealmRole;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RevisionedCachedRealmRole extends CachedRealmRole implements Revisioned {
|
||||
|
||||
public RevisionedCachedRealmRole(Long revision, RoleModel model, RealmModel realm) {
|
||||
super(model, realm);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
private Long revision;
|
||||
|
||||
@Override
|
||||
public Long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevision(Long revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package org.keycloak.models.cache.infinispan.locking.entities;
|
||||
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.cache.entities.CachedUser;
|
||||
import org.keycloak.models.cache.infinispan.locking.Revisioned;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RevisionedCachedUser extends CachedUser implements Revisioned {
|
||||
public RevisionedCachedUser(Long revision, RealmModel realm, UserModel user) {
|
||||
super(realm, user);
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
private Long revision;
|
||||
|
||||
@Override
|
||||
public Long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRevision(Long revision) {
|
||||
this.revision = revision;
|
||||
}
|
||||
|
||||
}
|
48
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientQueryPredicate.java
vendored
Executable file
48
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientQueryPredicate.java
vendored
Executable file
|
@ -0,0 +1,48 @@
|
|||
package org.keycloak.models.cache.infinispan.stream;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.cache.infinispan.entities.ClientQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientQueryPredicate implements Predicate<Map.Entry<String, Revisioned>>, Serializable {
|
||||
protected static final Logger logger = Logger.getLogger(ClientQueryPredicate.class);
|
||||
private String client;
|
||||
private String inRealm;
|
||||
|
||||
public static ClientQueryPredicate create() {
|
||||
return new ClientQueryPredicate();
|
||||
}
|
||||
|
||||
public ClientQueryPredicate client(String client) {
|
||||
this.client = client;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientQueryPredicate inRealm(String inRealm) {
|
||||
this.inRealm = inRealm;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, Revisioned> entry) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) return false;
|
||||
if (!(value instanceof ClientQuery)) return false;
|
||||
ClientQuery query = (ClientQuery)value;
|
||||
if (client != null && !query.getClients().contains(client)) return false;
|
||||
if (inRealm != null && !query.getRealm().equals(inRealm)) return false;
|
||||
return true;
|
||||
}
|
||||
}
|
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientTemplateQueryPredicate.java
vendored
Executable file
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/ClientTemplateQueryPredicate.java
vendored
Executable file
|
@ -0,0 +1,40 @@
|
|||
package org.keycloak.models.cache.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.ClientTemplateQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ClientTemplateQueryPredicate implements Predicate<Map.Entry<String, Revisioned>>, Serializable {
|
||||
private String template;
|
||||
|
||||
public static ClientTemplateQueryPredicate create() {
|
||||
return new ClientTemplateQueryPredicate();
|
||||
}
|
||||
|
||||
public ClientTemplateQueryPredicate template(String template) {
|
||||
this.template = template;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, Revisioned> entry) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) return false;
|
||||
if (!(value instanceof ClientTemplateQuery)) return false;
|
||||
ClientTemplateQuery query = (ClientTemplateQuery)value;
|
||||
|
||||
|
||||
return query.getTemplates().contains(template);
|
||||
}
|
||||
}
|
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/GroupQueryPredicate.java
vendored
Executable file
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/GroupQueryPredicate.java
vendored
Executable file
|
@ -0,0 +1,40 @@
|
|||
package org.keycloak.models.cache.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.GroupQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class GroupQueryPredicate implements Predicate<Map.Entry<String, Revisioned>>, Serializable {
|
||||
private String group;
|
||||
|
||||
public static GroupQueryPredicate create() {
|
||||
return new GroupQueryPredicate();
|
||||
}
|
||||
|
||||
public GroupQueryPredicate group(String group) {
|
||||
this.group = group;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, Revisioned> entry) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) return false;
|
||||
if (!(value instanceof GroupQuery)) return false;
|
||||
GroupQuery query = (GroupQuery)value;
|
||||
|
||||
|
||||
return query.getGroups().contains(group);
|
||||
}
|
||||
}
|
46
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java
vendored
Executable file
46
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/HasRolePredicate.java
vendored
Executable file
|
@ -0,0 +1,46 @@
|
|||
package org.keycloak.models.cache.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedGroup;
|
||||
import org.keycloak.models.cache.infinispan.entities.CachedRole;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.entities.RoleQuery;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class HasRolePredicate implements Predicate<Map.Entry<String, Revisioned>>, Serializable {
|
||||
private String role;
|
||||
|
||||
public static HasRolePredicate create() {
|
||||
return new HasRolePredicate();
|
||||
}
|
||||
|
||||
public HasRolePredicate role(String role) {
|
||||
this.role = role;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, Revisioned> entry) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) return false;
|
||||
if (value instanceof CachedRole) {
|
||||
CachedRole cachedRole = (CachedRole)value;
|
||||
if (cachedRole.getComposites().contains(role)) return true;
|
||||
}
|
||||
if (value instanceof CachedGroup) {
|
||||
CachedGroup cachedRole = (CachedGroup)value;
|
||||
if (cachedRole.getRoleMappings().contains(role)) return true;
|
||||
}
|
||||
if (value instanceof RoleQuery) {
|
||||
RoleQuery roleQuery = (RoleQuery)value;
|
||||
if (roleQuery.getRoles().contains(role)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
34
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InClientPredicate.java
vendored
Executable file
34
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InClientPredicate.java
vendored
Executable file
|
@ -0,0 +1,34 @@
|
|||
package org.keycloak.models.cache.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.InClient;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class InClientPredicate implements Predicate<Map.Entry<String, Revisioned>>, Serializable {
|
||||
private String clientId;
|
||||
|
||||
public static InClientPredicate create() {
|
||||
return new InClientPredicate();
|
||||
}
|
||||
|
||||
public InClientPredicate client(String id) {
|
||||
clientId = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, Revisioned> entry) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) return false;
|
||||
if (!(value instanceof InClient)) return false;
|
||||
|
||||
return clientId.equals(((InClient)value).getClientId());
|
||||
}
|
||||
}
|
34
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InRealmPredicate.java
vendored
Executable file
34
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/InRealmPredicate.java
vendored
Executable file
|
@ -0,0 +1,34 @@
|
|||
package org.keycloak.models.cache.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.InRealm;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class InRealmPredicate implements Predicate<Map.Entry<String, Revisioned>>, Serializable {
|
||||
private String realm;
|
||||
|
||||
public static InRealmPredicate create() {
|
||||
return new InRealmPredicate();
|
||||
}
|
||||
|
||||
public InRealmPredicate realm(String id) {
|
||||
realm = id;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, Revisioned> entry) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) return false;
|
||||
if (!(value instanceof InRealm)) return false;
|
||||
|
||||
return realm.equals(((InRealm)value).getRealm());
|
||||
}
|
||||
}
|
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmQueryPredicate.java
vendored
Executable file
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RealmQueryPredicate.java
vendored
Executable file
|
@ -0,0 +1,40 @@
|
|||
package org.keycloak.models.cache.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.RealmQuery;
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RealmQueryPredicate implements Predicate<Map.Entry<String, Revisioned>>, Serializable {
|
||||
private String realm;
|
||||
|
||||
public static RealmQueryPredicate create() {
|
||||
return new RealmQueryPredicate();
|
||||
}
|
||||
|
||||
public RealmQueryPredicate realm(String realm) {
|
||||
this.realm = realm;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, Revisioned> entry) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) return false;
|
||||
if (!(value instanceof RealmQuery)) return false;
|
||||
RealmQuery query = (RealmQuery)value;
|
||||
|
||||
|
||||
return query.getRealms().contains(realm);
|
||||
}
|
||||
}
|
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RoleQueryPredicate.java
vendored
Executable file
40
model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/stream/RoleQueryPredicate.java
vendored
Executable file
|
@ -0,0 +1,40 @@
|
|||
package org.keycloak.models.cache.infinispan.stream;
|
||||
|
||||
import org.keycloak.models.cache.infinispan.entities.Revisioned;
|
||||
import org.keycloak.models.cache.infinispan.entities.RoleQuery;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class RoleQueryPredicate implements Predicate<Map.Entry<String, Revisioned>>, Serializable {
|
||||
private String role;
|
||||
|
||||
public static RoleQueryPredicate create() {
|
||||
return new RoleQueryPredicate();
|
||||
}
|
||||
|
||||
public RoleQueryPredicate role(String role) {
|
||||
this.role = role;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean test(Map.Entry<String, Revisioned> entry) {
|
||||
Object value = entry.getValue();
|
||||
if (value == null) return false;
|
||||
if (!(value instanceof RoleQuery)) return false;
|
||||
RoleQuery query = (RoleQuery)value;
|
||||
|
||||
|
||||
return query.getRoles().contains(role);
|
||||
}
|
||||
}
|
|
@ -16,4 +16,3 @@
|
|||
#
|
||||
|
||||
org.keycloak.connections.infinispan.DefaultInfinispanConnectionProviderFactory
|
||||
org.keycloak.models.cache.infinispan.locking.LockingConnectionProviderFactory
|
|
@ -16,4 +16,3 @@
|
|||
#
|
||||
|
||||
org.keycloak.models.cache.infinispan.InfinispanCacheRealmProviderFactory
|
||||
org.keycloak.models.cache.infinispan.locking.LockingCacheRealmProviderFactory
|
|
@ -0,0 +1,112 @@
|
|||
package org.keycloak.models.sessions.infinispan.initializer;
|
||||
|
||||
import org.infinispan.Cache;
|
||||
import org.infinispan.configuration.cache.CacheMode;
|
||||
import org.infinispan.configuration.cache.Configuration;
|
||||
import org.infinispan.configuration.cache.ConfigurationBuilder;
|
||||
import org.infinispan.configuration.global.GlobalConfigurationBuilder;
|
||||
import org.infinispan.manager.DefaultCacheManager;
|
||||
import org.infinispan.manager.EmbeddedCacheManager;
|
||||
import org.infinispan.notifications.Listener;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated;
|
||||
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent;
|
||||
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||
|
||||
/**
|
||||
* Just a simple test to see how distributed caches work
|
||||
*
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
@Ignore
|
||||
public class ClusteredCacheBehaviorTest {
|
||||
public EmbeddedCacheManager createManager() {
|
||||
GlobalConfigurationBuilder gcb = new GlobalConfigurationBuilder();
|
||||
|
||||
boolean clustered = true;
|
||||
boolean async = false;
|
||||
boolean allowDuplicateJMXDomains = true;
|
||||
|
||||
if (clustered) {
|
||||
gcb.transport().defaultTransport();
|
||||
}
|
||||
gcb.globalJmxStatistics().allowDuplicateDomains(allowDuplicateJMXDomains);
|
||||
|
||||
EmbeddedCacheManager cacheManager = new DefaultCacheManager(gcb.build());
|
||||
|
||||
|
||||
ConfigurationBuilder invalidationConfigBuilder = new ConfigurationBuilder();
|
||||
if (clustered) {
|
||||
invalidationConfigBuilder.clustering().cacheMode(async ? CacheMode.INVALIDATION_ASYNC : CacheMode.INVALIDATION_SYNC);
|
||||
}
|
||||
Configuration invalidationCacheConfiguration = invalidationConfigBuilder.build();
|
||||
|
||||
cacheManager.defineConfiguration(InfinispanConnectionProvider.REALM_CACHE_NAME, invalidationCacheConfiguration);
|
||||
return cacheManager;
|
||||
|
||||
}
|
||||
|
||||
@Listener
|
||||
public static class CacheListener {
|
||||
String name;
|
||||
|
||||
public CacheListener(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@CacheEntryRemoved
|
||||
public void removed(CacheEntryRemovedEvent<String, Object> event) {
|
||||
System.out.println("Listener '" + name + "' entry removed isPre: " + event.isPre());
|
||||
}
|
||||
|
||||
@CacheEntryInvalidated
|
||||
public void removed(CacheEntryInvalidatedEvent<String, Object> event) {
|
||||
System.out.println("Listener '" + name + "' entry invalidated: isPre: " + event.isPre());
|
||||
}
|
||||
|
||||
@CacheEntriesEvicted
|
||||
public void evicted(CacheEntriesEvictedEvent<String, Object> event) {
|
||||
System.out.println("Listener '" + name + "' entry evicted isPre: " + event.isPre());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListener() throws Exception {
|
||||
EmbeddedCacheManager node1 = createManager();
|
||||
EmbeddedCacheManager node2 = createManager();
|
||||
Cache<String, Object> node1Cache = node1.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
|
||||
node1Cache.addListener(new CacheListener("node1"));
|
||||
Cache<String, Object> node2Cache = node2.getCache(InfinispanConnectionProvider.REALM_CACHE_NAME);
|
||||
node2Cache.addListener(new CacheListener("node2"));
|
||||
|
||||
System.out.println("node1 create entry");
|
||||
node1Cache.put("key", "node1");
|
||||
System.out.println("node2 create entry");
|
||||
node2Cache.put("key", "node2");
|
||||
System.out.println("node1 remove entry");
|
||||
node1Cache.remove("key");
|
||||
System.out.println("node2 remove entry");
|
||||
node2Cache.remove("key");
|
||||
System.out.println("node2 put entry");
|
||||
node2Cache.put("key", "node2");
|
||||
System.out.println("node2 evict entry");
|
||||
node2Cache.evict("key");
|
||||
System.out.println("node1/node2 putExternal entry");
|
||||
node1Cache.putForExternalRead("key", "common");
|
||||
node2Cache.putForExternalRead("key", "common");
|
||||
System.out.println("node2 remove entry");
|
||||
node2Cache.remove("key");
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -612,31 +612,17 @@ public class ClientAdapter implements ClientModel {
|
|||
|
||||
@Override
|
||||
public RoleModel getRole(String name) {
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("getClientRoleByName", RoleEntity.class);
|
||||
query.setParameter("name", name);
|
||||
query.setParameter("client", entity);
|
||||
List<RoleEntity> roles = query.getResultList();
|
||||
if (roles.size() == 0) return null;
|
||||
return new RoleAdapter(realm, em, roles.get(0));
|
||||
return session.realms().getClientRole(realm, this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
return this.addRole(KeycloakModelUtils.generateId(), name);
|
||||
return session.realms().addClientRole(realm, this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String id, String name) {
|
||||
RoleEntity roleEntity = new RoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setClient(entity);
|
||||
roleEntity.setClientRole(true);
|
||||
roleEntity.setRealmId(realm.getId());
|
||||
entity.getRoles().add(roleEntity);
|
||||
em.persist(roleEntity);
|
||||
em.flush();
|
||||
return new RoleAdapter(realm, em, roleEntity);
|
||||
return session.realms().addClientRole(realm, this, id, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -650,7 +636,6 @@ public class ClientAdapter implements ClientModel {
|
|||
RoleEntity role = RoleAdapter.toRoleEntity(roleModel, em);
|
||||
if (!role.isClientRole()) return false;
|
||||
|
||||
entity.getRoles().remove(role);
|
||||
entity.getDefaultRoles().remove(role);
|
||||
String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
|
||||
em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", role).executeUpdate();
|
||||
|
@ -666,22 +651,7 @@ public class ClientAdapter implements ClientModel {
|
|||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles() {
|
||||
Set<RoleModel> list = new HashSet<RoleModel>();
|
||||
/*
|
||||
Collection<RoleEntity> roles = entity.getRoles();
|
||||
if (roles == null) return list;
|
||||
for (RoleEntity entity : roles) {
|
||||
list.add(new RoleAdapter(realm, em, entity));
|
||||
}
|
||||
*/
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("getClientRoles", RoleEntity.class);
|
||||
query.setParameter("client", entity);
|
||||
List<RoleEntity> roles = query.getResultList();
|
||||
for (RoleEntity roleEntity : roles) {
|
||||
list.add(new RoleAdapter(realm, em, roleEntity));
|
||||
}
|
||||
return list;
|
||||
|
||||
return session.realms().getClientRoles(realm, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -731,10 +701,10 @@ public class ClientAdapter implements ClientModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateDefaultRoles(String[] defaultRoles) {
|
||||
public void updateDefaultRoles(String... defaultRoles) {
|
||||
Collection<RoleEntity> entities = entity.getDefaultRoles();
|
||||
Set<String> already = new HashSet<String>();
|
||||
List<RoleEntity> remove = new ArrayList<RoleEntity>();
|
||||
List<RoleEntity> remove = new ArrayList<>();
|
||||
for (RoleEntity rel : entities) {
|
||||
if (!contains(rel.getName(), defaultRoles)) {
|
||||
remove.add(rel);
|
||||
|
@ -754,6 +724,24 @@ public class ClientAdapter implements ClientModel {
|
|||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDefaultRoles(String... defaultRoles) {
|
||||
Collection<RoleEntity> entities = entity.getDefaultRoles();
|
||||
List<RoleEntity> remove = new ArrayList<RoleEntity>();
|
||||
for (RoleEntity rel : entities) {
|
||||
if (contains(rel.getName(), defaultRoles)) {
|
||||
remove.add(rel);
|
||||
}
|
||||
}
|
||||
for (RoleEntity entity : remove) {
|
||||
entities.remove(entity);
|
||||
}
|
||||
em.flush();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getNodeReRegistrationTimeout() {
|
||||
return entity.getNodeReRegistrationTimeout();
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
package org.keycloak.models.jpa;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||
import org.keycloak.migration.MigrationModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientTemplateModel;
|
||||
|
@ -25,6 +26,7 @@ import org.keycloak.models.GroupModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RealmProvider;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.jpa.entities.ClientEntity;
|
||||
import org.keycloak.models.jpa.entities.ClientTemplateEntity;
|
||||
|
@ -36,8 +38,11 @@ import org.keycloak.models.utils.KeycloakModelUtils;
|
|||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -90,25 +95,27 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
|
||||
@Override
|
||||
public List<RealmModel> getRealms() {
|
||||
TypedQuery<RealmEntity> query = em.createNamedQuery("getAllRealms", RealmEntity.class);
|
||||
List<RealmEntity> entities = query.getResultList();
|
||||
TypedQuery<String> query = em.createNamedQuery("getAllRealmIds", String.class);
|
||||
List<String> entities = query.getResultList();
|
||||
List<RealmModel> realms = new ArrayList<RealmModel>();
|
||||
for (RealmEntity entity : entities) {
|
||||
realms.add(new RealmAdapter(session, em, entity));
|
||||
for (String id : entities) {
|
||||
RealmModel realm = session.realms().getRealm(id);
|
||||
if (realm != null) realms.add(realm);
|
||||
|
||||
}
|
||||
return realms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RealmModel getRealmByName(String name) {
|
||||
TypedQuery<RealmEntity> query = em.createNamedQuery("getRealmByName", RealmEntity.class);
|
||||
TypedQuery<String> query = em.createNamedQuery("getRealmIdByName", String.class);
|
||||
query.setParameter("name", name);
|
||||
List<RealmEntity> entities = query.getResultList();
|
||||
List<String> entities = query.getResultList();
|
||||
if (entities.size() == 0) return null;
|
||||
if (entities.size() > 1) throw new IllegalStateException("Should not be more than one realm with same name");
|
||||
RealmEntity realm = query.getResultList().get(0);
|
||||
if (realm == null) return null;
|
||||
return new RealmAdapter(session, em, realm);
|
||||
String id = query.getResultList().get(0);
|
||||
|
||||
return session.realms().getRealm(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -126,30 +133,25 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
num = em.createNamedQuery("deleteGroupsByRealm")
|
||||
.setParameter("realm", realm).executeUpdate();
|
||||
|
||||
TypedQuery<ClientEntity> query = em.createNamedQuery("getClientsByRealm", ClientEntity.class);
|
||||
query.setParameter("realm", realm);
|
||||
List<ClientEntity> clients = query.getResultList();
|
||||
for (ClientEntity a : clients) {
|
||||
adapter.removeClient(a.getId());
|
||||
TypedQuery<String> query = em.createNamedQuery("getClientIdsByRealm", String.class);
|
||||
query.setParameter("realm", realm.getId());
|
||||
List<String> clients = query.getResultList();
|
||||
for (String client : clients) {
|
||||
session.realms().removeClient(client, adapter);
|
||||
}
|
||||
/*
|
||||
for (ClientEntity a : new LinkedList<>(realm.getClients())) {
|
||||
adapter.removeClient(a.getId());
|
||||
}
|
||||
*/
|
||||
|
||||
for (ClientTemplateEntity a : new LinkedList<>(realm.getClientTemplates())) {
|
||||
adapter.removeClientTemplate(a.getId());
|
||||
}
|
||||
|
||||
for (RoleModel role : adapter.getRoles()) {
|
||||
session.realms().removeRole(adapter, role);
|
||||
}
|
||||
|
||||
em.remove(realm);
|
||||
|
||||
em.flush();
|
||||
em.clear();
|
||||
realm = em.find(RealmEntity.class, id);
|
||||
if (realm != null) {
|
||||
logger.error("WTF is the realm still there after a removal????????");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -157,12 +159,118 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
public void close() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRealmRole(RealmModel realm, String name) {
|
||||
return addRealmRole(realm, KeycloakModelUtils.generateId(), name);
|
||||
|
||||
}
|
||||
@Override
|
||||
public RoleModel addRealmRole(RealmModel realm, String id, String name) {
|
||||
RoleEntity entity = new RoleEntity();
|
||||
entity.setId(id);
|
||||
entity.setName(name);
|
||||
RealmEntity ref = em.getReference(RealmEntity.class, realm.getId());
|
||||
entity.setRealm(ref);
|
||||
entity.setRealmId(realm.getId());
|
||||
em.persist(entity);
|
||||
em.flush();
|
||||
return new RoleAdapter(session, realm, em, entity);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRealmRole(RealmModel realm, String name) {
|
||||
TypedQuery<String> query = em.createNamedQuery("getRealmRoleIdByName", String.class);
|
||||
query.setParameter("name", name);
|
||||
query.setParameter("realm", realm.getId());
|
||||
List<String> roles = query.getResultList();
|
||||
if (roles.size() == 0) return null;
|
||||
return session.realms().getRoleById(roles.get(0), realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
|
||||
}
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
|
||||
ClientEntity clientEntity = em.getReference(ClientEntity.class, client.getId());
|
||||
RoleEntity roleEntity = new RoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setClient(clientEntity);
|
||||
roleEntity.setClientRole(true);
|
||||
roleEntity.setRealmId(realm.getId());
|
||||
em.persist(roleEntity);
|
||||
em.flush();
|
||||
return new RoleAdapter(session, realm, em, roleEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRealmRoles(RealmModel realm) {
|
||||
TypedQuery<String> query = em.createNamedQuery("getRealmRoleIds", String.class);
|
||||
query.setParameter("realm", realm.getId());
|
||||
List<String> roles = query.getResultList();
|
||||
|
||||
if (roles.isEmpty()) return Collections.EMPTY_SET;
|
||||
Set<RoleModel> list = new HashSet<RoleModel>();
|
||||
for (String id : roles) {
|
||||
list.add(session.realms().getRoleById(id, realm));
|
||||
}
|
||||
return Collections.unmodifiableSet(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
TypedQuery<String> query = em.createNamedQuery("getClientRoleIdByName", String.class);
|
||||
query.setParameter("name", name);
|
||||
query.setParameter("client", client.getId());
|
||||
List<String> roles = query.getResultList();
|
||||
if (roles.size() == 0) return null;
|
||||
return session.realms().getRoleById(roles.get(0), realm);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
|
||||
Set<RoleModel> list = new HashSet<RoleModel>();
|
||||
TypedQuery<String> query = em.createNamedQuery("getClientRoleIds", String.class);
|
||||
query.setParameter("client", client.getId());
|
||||
List<String> roles = query.getResultList();
|
||||
for (String id : roles) {
|
||||
list.add(session.realms().getRoleById(id, realm));
|
||||
}
|
||||
return list;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRole(RealmModel realm, RoleModel role) {
|
||||
session.users().preRemove(realm, role);
|
||||
RoleEntity roleEntity = em.getReference(RoleEntity.class, role.getId());
|
||||
RoleContainerModel container = role.getContainer();
|
||||
if (container.getDefaultRoles().contains(role.getName())) {
|
||||
container.removeDefaultRoles(role.getName());
|
||||
}
|
||||
String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
|
||||
em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate();
|
||||
em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
|
||||
em.createNamedQuery("deleteTemplateScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
|
||||
em.createNamedQuery("deleteGroupRoleMappingsByRole").setParameter("roleId", roleEntity.getId()).executeUpdate();
|
||||
|
||||
em.remove(roleEntity);
|
||||
em.flush();
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id, RealmModel realm) {
|
||||
RoleEntity entity = em.find(RoleEntity.class, id);
|
||||
if (entity == null) return null;
|
||||
if (!realm.getId().equals(entity.getRealmId())) return null;
|
||||
return new RoleAdapter(realm, em, entity);
|
||||
return new RoleAdapter(session, realm, em, entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -173,6 +281,52 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
return new GroupAdapter(realm, em, groupEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String clientId) {
|
||||
return addClient(realm, KeycloakModelUtils.generateId(), clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String id, String clientId) {
|
||||
if (clientId == null) {
|
||||
clientId = id;
|
||||
}
|
||||
ClientEntity entity = new ClientEntity();
|
||||
entity.setId(id);
|
||||
entity.setClientId(clientId);
|
||||
entity.setEnabled(true);
|
||||
entity.setStandardFlowEnabled(true);
|
||||
RealmEntity realmRef = em.getReference(RealmEntity.class, realm.getId());
|
||||
entity.setRealm(realmRef);
|
||||
em.persist(entity);
|
||||
em.flush();
|
||||
final ClientModel resource = new ClientAdapter(realm, em, session, entity);
|
||||
em.flush();
|
||||
session.getKeycloakSessionFactory().publish(new RealmModel.ClientCreationEvent() {
|
||||
@Override
|
||||
public ClientModel getCreatedClient() {
|
||||
return resource;
|
||||
}
|
||||
});
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientModel> getClients(RealmModel realm) {
|
||||
TypedQuery<String> query = em.createNamedQuery("getClientIdsByRealm", String.class);
|
||||
query.setParameter("realm", realm.getId());
|
||||
List<String> clients = query.getResultList();
|
||||
if (clients.isEmpty()) return Collections.EMPTY_LIST;
|
||||
List<ClientModel> list = new LinkedList<>();
|
||||
for (String id : clients) {
|
||||
ClientModel client = session.realms().getClientById(id, realm);
|
||||
if (client != null) list.add(client);
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ClientModel getClientById(String id, RealmModel realm) {
|
||||
ClientEntity app = em.find(ClientEntity.class, id);
|
||||
|
@ -184,13 +338,13 @@ public class JpaRealmProvider implements RealmProvider {
|
|||
|
||||
@Override
|
||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||
TypedQuery<ClientEntity> query = em.createNamedQuery("findClientByClientId", ClientEntity.class);
|
||||
TypedQuery<String> query = em.createNamedQuery("findClientIdByClientId", String.class);
|
||||
query.setParameter("clientId", clientId);
|
||||
query.setParameter("realm", realm.getId());
|
||||
List<ClientEntity> results = query.getResultList();
|
||||
List<String> results = query.getResultList();
|
||||
if (results.isEmpty()) return null;
|
||||
ClientEntity entity = results.get(0);
|
||||
return new ClientAdapter(realm, em, session, entity);
|
||||
String id = results.get(0);
|
||||
return session.realms().getClientById(id, realm);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -682,6 +682,21 @@ public class RealmAdapter implements RealmModel {
|
|||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDefaultRoles(String... defaultRoles) {
|
||||
Collection<RoleEntity> entities = realm.getDefaultRoles();
|
||||
List<RoleEntity> remove = new ArrayList<RoleEntity>();
|
||||
for (RoleEntity rel : entities) {
|
||||
if (contains(rel.getName(), defaultRoles)) {
|
||||
remove.add(rel);
|
||||
}
|
||||
}
|
||||
for (RoleEntity entity : remove) {
|
||||
entities.remove(entity);
|
||||
}
|
||||
em.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getDefaultGroups() {
|
||||
Collection<GroupEntity> entities = realm.getDefaultGroups();
|
||||
|
@ -723,45 +738,17 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public List<ClientModel> getClients() {
|
||||
TypedQuery<ClientEntity> query = em.createNamedQuery("getClientsByRealm", ClientEntity.class);
|
||||
query.setParameter("realm", realm);
|
||||
List<ClientEntity> clients = query.getResultList();
|
||||
if (clients.isEmpty()) return Collections.EMPTY_LIST;
|
||||
List<ClientModel> list = new LinkedList<>();
|
||||
for (ClientEntity entity : clients) {
|
||||
list.add(new ClientAdapter(this, em, session, entity));
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
return session.realms().getClients(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(String name) {
|
||||
return this.addClient(KeycloakModelUtils.generateId(), name);
|
||||
return session.realms().addClient(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(String id, String clientId) {
|
||||
if (clientId == null) {
|
||||
clientId = id;
|
||||
}
|
||||
ClientEntity entity = new ClientEntity();
|
||||
entity.setId(id);
|
||||
entity.setClientId(clientId);
|
||||
entity.setEnabled(true);
|
||||
entity.setStandardFlowEnabled(true);
|
||||
entity.setRealm(realm);
|
||||
realm.getClients().add(entity);
|
||||
em.persist(entity);
|
||||
em.flush();
|
||||
final ClientModel resource = new ClientAdapter(this, em, session, entity);
|
||||
em.flush();
|
||||
session.getKeycloakSessionFactory().publish(new ClientCreationEvent() {
|
||||
@Override
|
||||
public ClientModel getCreatedClient() {
|
||||
return resource;
|
||||
}
|
||||
});
|
||||
return resource;
|
||||
return session.realms().addClient(this, id, clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -956,7 +943,7 @@ public class RealmAdapter implements RealmModel {
|
|||
em.remove(entity);
|
||||
}
|
||||
|
||||
List<UserFederationProviderModel> add = new LinkedList<UserFederationProviderModel>();
|
||||
List<UserFederationProviderModel> add = new LinkedList<>();
|
||||
for (UserFederationProviderModel model : providers) {
|
||||
boolean found = false;
|
||||
for (UserFederationProviderEntity entity : realm.getUserFederationProviders()) {
|
||||
|
@ -1008,64 +995,27 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public RoleModel getRole(String name) {
|
||||
TypedQuery<RoleEntity> query = em.createNamedQuery("getRealmRoleByName", RoleEntity.class);
|
||||
query.setParameter("name", name);
|
||||
query.setParameter("realm", realm);
|
||||
List<RoleEntity> roles = query.getResultList();
|
||||
if (roles.size() == 0) return null;
|
||||
return new RoleAdapter(this, em, roles.get(0));
|
||||
return session.realms().getRealmRole(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
return this.addRole(KeycloakModelUtils.generateId(), name);
|
||||
return session.realms().addRealmRole(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String id, String name) {
|
||||
RoleEntity entity = new RoleEntity();
|
||||
entity.setId(id);
|
||||
entity.setName(name);
|
||||
entity.setRealm(realm);
|
||||
entity.setRealmId(realm.getId());
|
||||
realm.getRoles().add(entity);
|
||||
em.persist(entity);
|
||||
em.flush();
|
||||
return new RoleAdapter(this, em, entity);
|
||||
return session.realms().addRealmRole(this, id, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRole(RoleModel role) {
|
||||
if (role == null) {
|
||||
return false;
|
||||
}
|
||||
if (!role.getContainer().equals(this)) return false;
|
||||
session.users().preRemove(this, role);
|
||||
RoleEntity roleEntity = RoleAdapter.toRoleEntity(role, em);
|
||||
realm.getRoles().remove(roleEntity);
|
||||
realm.getDefaultRoles().remove(roleEntity);
|
||||
|
||||
String compositeRoleTable = JpaUtils.getTableNameForNativeQuery("COMPOSITE_ROLE", em);
|
||||
em.createNativeQuery("delete from " + compositeRoleTable + " where CHILD_ROLE = :role").setParameter("role", roleEntity).executeUpdate();
|
||||
em.createNamedQuery("deleteScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
|
||||
em.createNamedQuery("deleteTemplateScopeMappingByRole").setParameter("role", roleEntity).executeUpdate();
|
||||
em.createNamedQuery("deleteGroupRoleMappingsByRole").setParameter("roleId", roleEntity.getId()).executeUpdate();
|
||||
|
||||
em.remove(roleEntity);
|
||||
em.flush();
|
||||
|
||||
return true;
|
||||
return session.realms().removeRole(this, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles() {
|
||||
Collection<RoleEntity> roles = realm.getRoles();
|
||||
if (roles == null) return Collections.EMPTY_SET;
|
||||
Set<RoleModel> list = new HashSet<RoleModel>();
|
||||
for (RoleEntity entity : roles) {
|
||||
list.add(new RoleAdapter(this, em, entity));
|
||||
}
|
||||
return Collections.unmodifiableSet(list);
|
||||
return session.realms().getRealmRoles(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1259,9 +1209,14 @@ public class RealmAdapter implements RealmModel {
|
|||
if (masterAdminClient == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RealmAdapter masterRealm = new RealmAdapter(session, em, masterAdminClient.getRealm());
|
||||
return new ClientAdapter(masterRealm, em, session, masterAdminClient);
|
||||
RealmModel masterRealm = null;
|
||||
String masterAdminClientRealmId = masterAdminClient.getRealm().getId();
|
||||
if (masterAdminClientRealmId.equals(getId())) {
|
||||
masterRealm = this;
|
||||
} else {
|
||||
masterRealm = session.realms().getRealm(masterAdminClientRealmId);
|
||||
}
|
||||
return session.realms().getClientById(masterAdminClient.getId(), masterRealm);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2052,11 +2007,12 @@ public class RealmAdapter implements RealmModel {
|
|||
|
||||
@Override
|
||||
public List<GroupModel> getGroups() {
|
||||
List<GroupEntity> groups = em.createNamedQuery("getAllGroupsByRealm").setParameter("realm", realm).getResultList();
|
||||
List<String> groups = em.createNamedQuery("getAllGroupIdsByRealm", String.class)
|
||||
.setParameter("realm", realm.getId()).getResultList();
|
||||
if (groups == null) return Collections.EMPTY_LIST;
|
||||
List<GroupModel> list = new LinkedList<>();
|
||||
for (GroupEntity entity : groups) {
|
||||
list.add(new GroupAdapter(this, em, entity));
|
||||
for (String id : groups) {
|
||||
list.add(session.realms().getGroupById(id, this));
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
@ -2128,7 +2084,7 @@ public class RealmAdapter implements RealmModel {
|
|||
if (entities == null || entities.isEmpty()) return Collections.EMPTY_LIST;
|
||||
List<ClientTemplateModel> list = new LinkedList<>();
|
||||
for (ClientTemplateEntity entity : entities) {
|
||||
list.add(new ClientTemplateAdapter(this, em, session, entity));
|
||||
list.add(session.realms().getClientTemplateById(entity.getId(), this));
|
||||
}
|
||||
return Collections.unmodifiableList(list);
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.models.jpa;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleContainerModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
|
@ -36,11 +37,13 @@ public class RoleAdapter implements RoleModel {
|
|||
protected RoleEntity role;
|
||||
protected EntityManager em;
|
||||
protected RealmModel realm;
|
||||
protected KeycloakSession session;
|
||||
|
||||
public RoleAdapter(RealmModel realm, EntityManager em, RoleEntity role) {
|
||||
public RoleAdapter(KeycloakSession session, RealmModel realm, EntityManager em, RoleEntity role) {
|
||||
this.em = em;
|
||||
this.realm = realm;
|
||||
this.role = role;
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
public RoleEntity getRole() {
|
||||
|
@ -115,7 +118,10 @@ public class RoleAdapter implements RoleModel {
|
|||
Set<RoleModel> set = new HashSet<RoleModel>();
|
||||
|
||||
for (RoleEntity composite : getRole().getCompositeRoles()) {
|
||||
set.add(new RoleAdapter(realm, em, composite));
|
||||
set.add(new RoleAdapter(session, realm, em, composite));
|
||||
|
||||
// todo I want to do this, but can't as you get stack overflow
|
||||
// set.add(session.realms().getRoleById(composite.getId(), realm));
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ import java.util.Set;
|
|||
@Table(name="CLIENT", uniqueConstraints = {@UniqueConstraint(columnNames = {"REALM_ID", "CLIENT_ID"})})
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getClientsByRealm", query="select client from ClientEntity client where client.realm = :realm"),
|
||||
@NamedQuery(name="getClientIdsByRealm", query="select client.id from ClientEntity client where client.realm.id = :realm"),
|
||||
@NamedQuery(name="findClientIdByClientId", query="select client.id from ClientEntity client where client.clientId = :clientId and client.realm.id = :realm"),
|
||||
@NamedQuery(name="findClientByClientId", query="select client from ClientEntity client where client.clientId = :clientId and client.realm.id = :realm"),
|
||||
})
|
||||
|
@ -156,9 +157,6 @@ public class ClientEntity {
|
|||
@Column(name="NODE_REREG_TIMEOUT")
|
||||
private int nodeReRegistrationTimeout;
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, mappedBy = "client")
|
||||
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true)
|
||||
@JoinTable(name="CLIENT_DEFAULT_ROLES", joinColumns = { @JoinColumn(name="CLIENT_ID")}, inverseJoinColumns = { @JoinColumn(name="ROLE_ID")})
|
||||
Collection<RoleEntity> defaultRoles = new ArrayList<RoleEntity>();
|
||||
|
@ -353,14 +351,6 @@ public class ClientEntity {
|
|||
this.managementUrl = managementUrl;
|
||||
}
|
||||
|
||||
public Collection<RoleEntity> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Collection<RoleEntity> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public Collection<RoleEntity> getDefaultRoles() {
|
||||
return defaultRoles;
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import java.util.Collection;
|
|||
*/
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getAllGroupsByRealm", query="select u from GroupEntity u where u.realm = :realm order by u.name"),
|
||||
@NamedQuery(name="getAllGroupIdsByRealm", query="select u.id from GroupEntity u where u.realm.id = :realm order by u.name"),
|
||||
@NamedQuery(name="getGroupById", query="select u from GroupEntity u where u.id = :id and u.realm = :realm"),
|
||||
@NamedQuery(name="getGroupIdsByParent", query="select u.id from GroupEntity u where u.parent = :parent"),
|
||||
@NamedQuery(name="getGroupCount", query="select count(u) from GroupEntity u where u.realm = :realm"),
|
||||
|
|
|
@ -49,8 +49,8 @@ import java.util.Set;
|
|||
@Table(name="REALM")
|
||||
@Entity
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getAllRealms", query="select realm from RealmEntity realm"),
|
||||
@NamedQuery(name="getRealmByName", query="select realm from RealmEntity realm where realm.name = :name"),
|
||||
@NamedQuery(name="getAllRealmIds", query="select realm.id from RealmEntity realm"),
|
||||
@NamedQuery(name="getRealmIdByName", query="select realm.id from RealmEntity realm where realm.name = :name"),
|
||||
})
|
||||
public class RealmEntity {
|
||||
@Id
|
||||
|
@ -147,15 +147,9 @@ public class RealmEntity {
|
|||
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||
Collection<UserFederationMapperEntity> userFederationMappers = new ArrayList<UserFederationMapperEntity>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy="realm")
|
||||
Collection<ClientEntity> clients = new ArrayList<>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||
Collection<ClientTemplateEntity> clientTemplates = new ArrayList<>();
|
||||
|
||||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, mappedBy = "realm")
|
||||
Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
|
||||
|
||||
@ElementCollection
|
||||
@MapKeyColumn(name="NAME")
|
||||
@Column(name="VALUE")
|
||||
|
@ -421,29 +415,6 @@ public class RealmEntity {
|
|||
public void setRequiredCredentials(Collection<RequiredCredentialEntity> requiredCredentials) {
|
||||
this.requiredCredentials = requiredCredentials;
|
||||
}
|
||||
public Collection<ClientEntity> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public void setClients(Collection<ClientEntity> clients) {
|
||||
this.clients = clients;
|
||||
}
|
||||
|
||||
public Collection<RoleEntity> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public void setRoles(Collection<RoleEntity> roles) {
|
||||
this.roles = roles;
|
||||
}
|
||||
|
||||
public void addRole(RoleEntity role) {
|
||||
if (roles == null) {
|
||||
roles = new ArrayList<RoleEntity>();
|
||||
}
|
||||
roles.add(role);
|
||||
}
|
||||
|
||||
public Map<String, String> getSmtpConfig() {
|
||||
return smtpConfig;
|
||||
}
|
||||
|
|
|
@ -49,8 +49,13 @@ import java.util.Collection;
|
|||
})
|
||||
@NamedQueries({
|
||||
@NamedQuery(name="getClientRoles", query="select role from RoleEntity role where role.client = :client"),
|
||||
@NamedQuery(name="getClientRoleIds", query="select role.id from RoleEntity role where role.client.id = :client"),
|
||||
@NamedQuery(name="getClientRoleByName", query="select role from RoleEntity role where role.name = :name and role.client = :client"),
|
||||
@NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.clientRole = false and role.name = :name and role.realm = :realm")
|
||||
@NamedQuery(name="getClientRoleIdByName", query="select role.id from RoleEntity role where role.name = :name and role.client.id = :client"),
|
||||
@NamedQuery(name="getRealmRoles", query="select role from RoleEntity role where role.clientRole = false and role.realm = :realm"),
|
||||
@NamedQuery(name="getRealmRoleIds", query="select role.id from RoleEntity role where role.clientRole = false and role.realm.id = :realm"),
|
||||
@NamedQuery(name="getRealmRoleByName", query="select role from RoleEntity role where role.clientRole = false and role.name = :name and role.realm = :realm"),
|
||||
@NamedQuery(name="getRealmRoleIdByName", query="select role.id from RoleEntity role where role.clientRole = false and role.name = :name and role.realm.id = :realm")
|
||||
})
|
||||
|
||||
public class RoleEntity {
|
||||
|
@ -105,6 +110,8 @@ public class RoleEntity {
|
|||
this.realmId = realmId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
|
|
@ -567,34 +567,18 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
|
|||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter getRole(String name) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("name").is(name)
|
||||
.and("clientId").is(getId())
|
||||
.get();
|
||||
MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new RoleAdapter(session, getRealm(), role, invocationContext);
|
||||
}
|
||||
public RoleModel getRole(String name) {
|
||||
return session.realms().getClientRole(realm, this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter addRole(String name) {
|
||||
return this.addRole(null, name);
|
||||
public RoleModel addRole(String name) {
|
||||
return session.realms().addClientRole(realm, this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter addRole(String id, String name) {
|
||||
MongoRoleEntity roleEntity = new MongoRoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setClientId(getId());
|
||||
|
||||
getMongoStore().insertEntity(roleEntity, invocationContext);
|
||||
|
||||
return new RoleAdapter(session, getRealm(), roleEntity, this, invocationContext);
|
||||
public RoleModel addRole(String id, String name) {
|
||||
return session.realms().addClientRole(realm, this, id, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -605,17 +589,7 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
|
|||
|
||||
@Override
|
||||
public Set<RoleModel> getRoles() {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("clientId").is(getId())
|
||||
.get();
|
||||
List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
|
||||
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
for (MongoRoleEntity role : roles) {
|
||||
result.add(new RoleAdapter(session, getRealm(), role, this, invocationContext));
|
||||
}
|
||||
|
||||
return result;
|
||||
return session.realms().getClientRoles(realm, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -653,7 +627,7 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
|
|||
}
|
||||
|
||||
@Override
|
||||
public void updateDefaultRoles(String[] defaultRoles) {
|
||||
public void updateDefaultRoles(String... defaultRoles) {
|
||||
List<String> roleNames = new ArrayList<String>();
|
||||
for (String roleName : defaultRoles) {
|
||||
RoleModel role = getRole(roleName);
|
||||
|
@ -668,6 +642,17 @@ public class ClientAdapter extends AbstractMongoAdapter<MongoClientEntity> imple
|
|||
updateMongoEntity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDefaultRoles(String... defaultRoles) {
|
||||
List<String> roleNames = new ArrayList<String>();
|
||||
for (String role : getMongoEntity().getDefaultRoles()) {
|
||||
if (!RealmAdapter.contains(role, defaultRoles)) roleNames.add(role);
|
||||
}
|
||||
getMongoEntity().setDefaultRoles(roleNames);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getNodeReRegistrationTimeout() {
|
||||
return getMongoEntity().getNodeReRegistrationTimeout();
|
||||
|
|
|
@ -39,7 +39,10 @@ import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
|
|||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -105,7 +108,8 @@ public class MongoRealmProvider implements RealmProvider {
|
|||
|
||||
List<RealmModel> results = new ArrayList<RealmModel>();
|
||||
for (MongoRealmEntity realmEntity : realms) {
|
||||
results.add(new RealmAdapter(session, realmEntity, invocationContext));
|
||||
RealmModel realm = session.realms().getRealm(realmEntity.getId());
|
||||
if (realm != null) results.add(realm);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
@ -118,7 +122,7 @@ public class MongoRealmProvider implements RealmProvider {
|
|||
MongoRealmEntity realm = getMongoStore().loadSingleEntity(MongoRealmEntity.class, query, invocationContext);
|
||||
|
||||
if (realm == null) return null;
|
||||
return new RealmAdapter(session, realm, invocationContext);
|
||||
return session.realms().getRealm(realm.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -162,6 +166,152 @@ public class MongoRealmProvider implements RealmProvider {
|
|||
return new ClientAdapter(session, realm, appData, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String clientId) {
|
||||
return addClient(realm, KeycloakModelUtils.generateId(), clientId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(RealmModel realm, String id, String clientId) {
|
||||
MongoClientEntity clientEntity = new MongoClientEntity();
|
||||
clientEntity.setId(id);
|
||||
clientEntity.setClientId(clientId);
|
||||
clientEntity.setRealmId(realm.getId());
|
||||
clientEntity.setEnabled(true);
|
||||
clientEntity.setStandardFlowEnabled(true);
|
||||
getMongoStore().insertEntity(clientEntity, invocationContext);
|
||||
|
||||
if (clientId == null) {
|
||||
clientEntity.setClientId(clientEntity.getId());
|
||||
getMongoStore().updateEntity(clientEntity, invocationContext);
|
||||
}
|
||||
|
||||
final ClientModel model = new ClientAdapter(session, realm, clientEntity, invocationContext);
|
||||
session.getKeycloakSessionFactory().publish(new RealmModel.ClientCreationEvent() {
|
||||
@Override
|
||||
public ClientModel getCreatedClient() {
|
||||
return model;
|
||||
}
|
||||
});
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ClientModel> getClients(RealmModel realm) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(realm.getId())
|
||||
.get();
|
||||
List<MongoClientEntity> clientEntities = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext);
|
||||
|
||||
if (clientEntities.isEmpty()) return Collections.EMPTY_LIST;
|
||||
List<ClientModel> result = new ArrayList<ClientModel>();
|
||||
for (MongoClientEntity clientEntity : clientEntities) {
|
||||
result.add(session.realms().getClientById(clientEntity.getId(), realm));
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRealmRole(RealmModel realm, String name) {
|
||||
return addRealmRole(realm, KeycloakModelUtils.generateId(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRealmRole(RealmModel realm, String id, String name) {
|
||||
MongoRoleEntity roleEntity = new MongoRoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setRealmId(realm.getId());
|
||||
|
||||
getMongoStore().insertEntity(roleEntity, invocationContext);
|
||||
|
||||
return new RoleAdapter(session, realm, roleEntity, realm, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getRealmRoles(RealmModel realm) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(realm.getId())
|
||||
.get();
|
||||
List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
|
||||
|
||||
|
||||
if (roles == null) return Collections.EMPTY_SET;
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
for (MongoRoleEntity role : roles) {
|
||||
result.add(session.realms().getRoleById(role.getId(), realm));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(result);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("clientId").is(client.getId())
|
||||
.get();
|
||||
List<MongoRoleEntity> roles = getMongoStore().loadEntities(MongoRoleEntity.class, query, invocationContext);
|
||||
|
||||
Set<RoleModel> result = new HashSet<RoleModel>();
|
||||
for (MongoRoleEntity role : roles) {
|
||||
result.add(session.realms().getRoleById(role.getId(), realm));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getRealmRole(RealmModel realm, String name) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("name").is(name)
|
||||
.and("realmId").is(realm.getId())
|
||||
.get();
|
||||
MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return session.realms().getRoleById(role.getId(), realm);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel getClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("name").is(name)
|
||||
.and("clientId").is(client.getId())
|
||||
.get();
|
||||
MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return session.realms().getRoleById(role.getId(), realm);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String name) {
|
||||
return addClientRole(realm, client, KeycloakModelUtils.generateId(), name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name) {
|
||||
MongoRoleEntity roleEntity = new MongoRoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setClientId(client.getId());
|
||||
|
||||
getMongoStore().insertEntity(roleEntity, invocationContext);
|
||||
|
||||
return new RoleAdapter(session, realm, roleEntity, client, invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRole(RealmModel realm, RoleModel role) {
|
||||
session.users().preRemove(realm, role);
|
||||
return getMongoStore().removeEntity(MongoRoleEntity.class, role.getId(), invocationContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeClient(String id, RealmModel realm) {
|
||||
if (id == null) return false;
|
||||
|
@ -180,7 +330,8 @@ public class MongoRealmProvider implements RealmProvider {
|
|||
.and("clientId").is(clientId)
|
||||
.get();
|
||||
MongoClientEntity appEntity = getMongoStore().loadSingleEntity(MongoClientEntity.class, query, invocationContext);
|
||||
return appEntity == null ? null : new ClientAdapter(session, realm, appEntity, invocationContext);
|
||||
if (appEntity == null) return null;
|
||||
return session.realms().getClientById(appEntity.getId(), realm);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -595,47 +595,30 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
}
|
||||
|
||||
@Override
|
||||
public RoleAdapter getRole(String name) {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("name").is(name)
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
MongoRoleEntity role = getMongoStore().loadSingleEntity(MongoRoleEntity.class, query, invocationContext);
|
||||
if (role == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new RoleAdapter(session, this, role, this, invocationContext);
|
||||
}
|
||||
public RoleModel getRole(String name) {
|
||||
return session.realms().getRealmRole(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String name) {
|
||||
return this.addRole(null, name);
|
||||
return session.realms().addRealmRole(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RoleModel addRole(String id, String name) {
|
||||
MongoRoleEntity roleEntity = new MongoRoleEntity();
|
||||
roleEntity.setId(id);
|
||||
roleEntity.setName(name);
|
||||
roleEntity.setRealmId(getId());
|
||||
|
||||
getMongoStore().insertEntity(roleEntity, invocationContext);
|
||||
|
||||
return new RoleAdapter(session, this, roleEntity, this, invocationContext);
|
||||
return session.realms().addRealmRole(this, id, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRole(RoleModel role) {
|
||||
return removeRoleById(role.getId());
|
||||
return session.realms().removeRole(this, role);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeRoleById(String id) {
|
||||
RoleModel role = getRoleById(id);
|
||||
if (role == null) return false;
|
||||
session.users().preRemove(this, role);
|
||||
return getMongoStore().removeEntity(MongoRoleEntity.class, id, invocationContext);
|
||||
return removeRole(role);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -657,7 +640,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
|
||||
@Override
|
||||
public RoleModel getRoleById(String id) {
|
||||
return model.getRoleById(id, this);
|
||||
return session.realms().getRoleById(id, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -780,6 +763,24 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
updateRealm();
|
||||
}
|
||||
|
||||
public static boolean contains(String str, String[] array) {
|
||||
for (String s : array) {
|
||||
if (str.equals(s)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void removeDefaultRoles(String... defaultRoles) {
|
||||
List<String> roleNames = new ArrayList<String>();
|
||||
for (String role : realm.getDefaultRoles()) {
|
||||
if (!contains(role, defaultRoles)) roleNames.add(role);
|
||||
}
|
||||
realm.setDefaultRoles(roleNames);
|
||||
updateRealm();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GroupModel> getDefaultGroups() {
|
||||
List<String> entities = realm.getDefaultGroups();
|
||||
|
@ -815,47 +816,18 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
|
||||
@Override
|
||||
public List<ClientModel> getClients() {
|
||||
DBObject query = new QueryBuilder()
|
||||
.and("realmId").is(getId())
|
||||
.get();
|
||||
List<MongoClientEntity> clientEntities = getMongoStore().loadEntities(MongoClientEntity.class, query, invocationContext);
|
||||
|
||||
if (clientEntities.isEmpty()) return Collections.EMPTY_LIST;
|
||||
List<ClientModel> result = new ArrayList<ClientModel>();
|
||||
for (MongoClientEntity clientEntity : clientEntities) {
|
||||
result.add(new ClientAdapter(session, this, clientEntity, invocationContext));
|
||||
}
|
||||
return Collections.unmodifiableList(result);
|
||||
return session.realms().getClients(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(String name) {
|
||||
return this.addClient(null, name);
|
||||
return session.realms().addClient(this, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientModel addClient(String id, String clientId) {
|
||||
MongoClientEntity clientEntity = new MongoClientEntity();
|
||||
clientEntity.setId(id);
|
||||
clientEntity.setClientId(clientId);
|
||||
clientEntity.setRealmId(getId());
|
||||
clientEntity.setEnabled(true);
|
||||
clientEntity.setStandardFlowEnabled(true);
|
||||
getMongoStore().insertEntity(clientEntity, invocationContext);
|
||||
return session.realms().addClient(this, id, clientId);
|
||||
|
||||
if (clientId == null) {
|
||||
clientEntity.setClientId(clientEntity.getId());
|
||||
getMongoStore().updateEntity(clientEntity, invocationContext);
|
||||
}
|
||||
|
||||
final ClientModel model = new ClientAdapter(session, this, clientEntity, invocationContext);
|
||||
session.getKeycloakSessionFactory().publish(new ClientCreationEvent() {
|
||||
@Override
|
||||
public ClientModel getCreatedClient() {
|
||||
return model;
|
||||
}
|
||||
});
|
||||
return model;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -85,12 +85,6 @@ public interface ClientModel extends RoleContainerModel, ProtocolMapperContaine
|
|||
|
||||
void setBaseUrl(String url);
|
||||
|
||||
List<String> getDefaultRoles();
|
||||
|
||||
void addDefaultRole(String name);
|
||||
|
||||
void updateDefaultRoles(String[] defaultRoles);
|
||||
|
||||
|
||||
boolean isBearerOnly();
|
||||
void setBearerOnly(boolean only);
|
||||
|
|
|
@ -181,12 +181,6 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
RoleModel getRoleById(String id);
|
||||
|
||||
List<String> getDefaultRoles();
|
||||
|
||||
void addDefaultRole(String name);
|
||||
|
||||
void updateDefaultRoles(String[] defaultRoles);
|
||||
|
||||
List<GroupModel> getDefaultGroups();
|
||||
|
||||
void addDefaultGroup(GroupModel group);
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.keycloak.migration.MigrationModel;
|
|||
import org.keycloak.provider.Provider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -35,10 +36,34 @@ public interface RealmProvider extends Provider {
|
|||
RealmModel getRealm(String id);
|
||||
RealmModel getRealmByName(String name);
|
||||
|
||||
ClientModel addClient(RealmModel realm, String clientId);
|
||||
|
||||
ClientModel addClient(RealmModel realm, String id, String clientId);
|
||||
|
||||
List<ClientModel> getClients(RealmModel realm);
|
||||
|
||||
ClientModel getClientById(String id, RealmModel realm);
|
||||
ClientModel getClientByClientId(String clientId, RealmModel realm);
|
||||
|
||||
|
||||
RoleModel addRealmRole(RealmModel realm, String name);
|
||||
|
||||
RoleModel addRealmRole(RealmModel realm, String id, String name);
|
||||
|
||||
RoleModel getRealmRole(RealmModel realm, String name);
|
||||
|
||||
RoleModel addClientRole(RealmModel realm, ClientModel client, String name);
|
||||
|
||||
RoleModel addClientRole(RealmModel realm, ClientModel client, String id, String name);
|
||||
|
||||
Set<RoleModel> getRealmRoles(RealmModel realm);
|
||||
|
||||
RoleModel getClientRole(RealmModel realm, ClientModel client, String name);
|
||||
|
||||
Set<RoleModel> getClientRoles(RealmModel realm, ClientModel client);
|
||||
|
||||
boolean removeRole(RealmModel realm, RoleModel role);
|
||||
|
||||
RoleModel getRoleById(String id, RealmModel realm);
|
||||
|
||||
boolean removeClient(String id, RealmModel realm);
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
package org.keycloak.models;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
|
@ -24,6 +25,8 @@ import java.util.Set;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public interface RoleContainerModel {
|
||||
String getId();
|
||||
|
||||
RoleModel getRole(String name);
|
||||
|
||||
RoleModel addRole(String name);
|
||||
|
@ -34,4 +37,11 @@ public interface RoleContainerModel {
|
|||
|
||||
Set<RoleModel> getRoles();
|
||||
|
||||
List<String> getDefaultRoles();
|
||||
|
||||
void addDefaultRole(String name);
|
||||
|
||||
void updateDefaultRoles(String... defaultRoles);
|
||||
|
||||
void removeDefaultRoles(String... defaultRoles);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public interface CacheRealmProvider extends RealmProvider {
|
|||
|
||||
void registerRealmInvalidation(String id);
|
||||
|
||||
void registerApplicationInvalidation(String id);
|
||||
void registerClientInvalidation(String id);
|
||||
void registerClientTemplateInvalidation(String id);
|
||||
|
||||
void registerRoleInvalidation(String id);
|
||||
|
|
|
@ -27,7 +27,10 @@
|
|||
},
|
||||
|
||||
"userCache": {
|
||||
"provider": "${keycloak.user.cache.provider:infinispan}",
|
||||
"provider": "${keycloak.user.cache.provider:default}",
|
||||
"default" : {
|
||||
"enabled": true
|
||||
},
|
||||
"mem": {
|
||||
"maxSize": 20000
|
||||
}
|
||||
|
@ -95,15 +98,14 @@
|
|||
},
|
||||
|
||||
"realmCache": {
|
||||
"provider": "infinispan-locking",
|
||||
"infinispan-locking" : {
|
||||
"provider": "${keycloak.realm.cache.provider:default}",
|
||||
"default" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
||||
"connectionsInfinispan": {
|
||||
"provider": "locking",
|
||||
"locking": {
|
||||
"default": {
|
||||
"clustered": "${keycloak.connectionsInfinispan.clustered:false}",
|
||||
"async": "${keycloak.connectionsInfinispan.async:true}",
|
||||
"sessionsOwners": "${keycloak.connectionsInfinispan.sessionsOwners:2}"
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.keycloak.admin.client.resource.RealmResource;
|
|||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -90,6 +91,7 @@ public class ConcurrencyTest extends AbstractClientTest {
|
|||
|
||||
@Test
|
||||
public void createClient() throws Throwable {
|
||||
System.out.println("***************************");
|
||||
long start = System.currentTimeMillis();
|
||||
run(new KeycloakRunnable() {
|
||||
@Override
|
||||
|
@ -101,7 +103,8 @@ public class ConcurrencyTest extends AbstractClientTest {
|
|||
String id = ApiUtil.getCreatedId(response);
|
||||
response.close();
|
||||
|
||||
assertNotNull(realm.clients().get(id));
|
||||
c = realm.clients().get(id).toRepresentation();
|
||||
assertNotNull(c);
|
||||
boolean found = false;
|
||||
for (ClientRepresentation r : realm.clients().findAll()) {
|
||||
if (r.getClientId().equals(name)) {
|
||||
|
@ -119,6 +122,58 @@ public class ConcurrencyTest extends AbstractClientTest {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void createRemoveClient() throws Throwable {
|
||||
// FYI< this will fail as HSQL seems to be trying to perform table locks.
|
||||
System.out.println("***************************");
|
||||
long start = System.currentTimeMillis();
|
||||
run(new KeycloakRunnable() {
|
||||
@Override
|
||||
public void run(Keycloak keycloak, RealmResource realm, int threadNum, int iterationNum) {
|
||||
String name = "c-" + threadNum + "-" + iterationNum;
|
||||
ClientRepresentation c = new ClientRepresentation();
|
||||
c.setClientId(name);
|
||||
Response response = realm.clients().create(c);
|
||||
String id = ApiUtil.getCreatedId(response);
|
||||
response.close();
|
||||
|
||||
c = realm.clients().get(id).toRepresentation();
|
||||
assertNotNull(c);
|
||||
boolean found = false;
|
||||
for (ClientRepresentation r : realm.clients().findAll()) {
|
||||
if (r.getClientId().equals(name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
fail("Client " + name + " not found in client list");
|
||||
}
|
||||
realm.clients().get(id).remove();
|
||||
try {
|
||||
c = realm.clients().get(id).toRepresentation();
|
||||
fail("Client " + name + " should not be found. Should throw a 404");
|
||||
} catch (NotFoundException e) {
|
||||
|
||||
}
|
||||
found = false;
|
||||
for (ClientRepresentation r : realm.clients().findAll()) {
|
||||
if (r.getClientId().equals(name)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
Assert.assertFalse("Client " + name + " should not be in client list", found);
|
||||
|
||||
}
|
||||
});
|
||||
long end = System.currentTimeMillis() - start;
|
||||
System.out.println("createClient took " + end);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void createRole() throws Throwable {
|
||||
long start = System.currentTimeMillis();
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
},
|
||||
|
||||
"userCache": {
|
||||
"infinispan" : {
|
||||
"default" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
@ -78,15 +78,13 @@
|
|||
},
|
||||
|
||||
"realmCache": {
|
||||
"provider": "infinispan-locking",
|
||||
"infinispan-locking" : {
|
||||
"default" : {
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
|
||||
"connectionsInfinispan": {
|
||||
"provider": "locking",
|
||||
"locking": {
|
||||
"default": {
|
||||
"clustered": "${keycloak.connectionsInfinispan.clustered:false}",
|
||||
"async": "${keycloak.connectionsInfinispan.async:true}",
|
||||
"sessionsOwners": "${keycloak.connectionsInfinispan.sessionsOwners:2}"
|
||||
|
|
|
@ -19,7 +19,7 @@ log4j.rootLogger=info, stdout
|
|||
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %t [%c] %m%n
|
||||
|
||||
log4j.logger.org.keycloak=info
|
||||
|
||||
|
|
Loading…
Reference in a new issue