caching
This commit is contained in:
parent
1d8e38f0c6
commit
4bf23cc83a
11 changed files with 445 additions and 233 deletions
|
@ -39,14 +39,12 @@ import java.util.Set;
|
||||||
public class ClientAdapter implements ClientModel {
|
public class ClientAdapter implements ClientModel {
|
||||||
protected RealmCacheSession cacheSession;
|
protected RealmCacheSession cacheSession;
|
||||||
protected RealmModel cachedRealm;
|
protected RealmModel cachedRealm;
|
||||||
protected RealmCache cache;
|
|
||||||
|
|
||||||
protected ClientModel updated;
|
protected ClientModel updated;
|
||||||
protected CachedClient cached;
|
protected CachedClient cached;
|
||||||
|
|
||||||
public ClientAdapter(RealmModel cachedRealm, CachedClient cached, RealmCacheSession cacheSession, RealmCache cache) {
|
public ClientAdapter(RealmModel cachedRealm, CachedClient cached, RealmCacheSession cacheSession) {
|
||||||
this.cachedRealm = cachedRealm;
|
this.cachedRealm = cachedRealm;
|
||||||
this.cache = cache;
|
|
||||||
this.cacheSession = cacheSession;
|
this.cacheSession = cacheSession;
|
||||||
this.cached = cached;
|
this.cached = cached;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.keycloak.models.*;
|
||||||
import org.keycloak.models.cache.CachedRealmModel;
|
import org.keycloak.models.cache.CachedRealmModel;
|
||||||
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
import org.keycloak.models.cache.infinispan.entities.CachedRealm;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
|
import org.keycloak.storage.client.ClientStorageProvider;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
@ -36,7 +37,6 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
protected CachedRealm cached;
|
protected CachedRealm cached;
|
||||||
protected RealmCacheSession cacheSession;
|
protected RealmCacheSession cacheSession;
|
||||||
protected volatile RealmModel updated;
|
protected volatile RealmModel updated;
|
||||||
protected RealmCache cache;
|
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
|
|
||||||
public RealmAdapter(KeycloakSession session, CachedRealm cached, RealmCacheSession cacheSession) {
|
public RealmAdapter(KeycloakSession session, CachedRealm cached, RealmCacheSession cacheSession) {
|
||||||
|
@ -1323,35 +1323,37 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
@Override
|
@Override
|
||||||
public ComponentModel addComponentModel(ComponentModel model) {
|
public ComponentModel addComponentModel(ComponentModel model) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
evictUsers(model);
|
executeEvictions(model);
|
||||||
return updated.addComponentModel(model);
|
return updated.addComponentModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ComponentModel importComponentModel(ComponentModel model) {
|
public ComponentModel importComponentModel(ComponentModel model) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
evictUsers(model);
|
executeEvictions(model);
|
||||||
return updated.importComponentModel(model);
|
return updated.importComponentModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void evictUsers(ComponentModel model) {
|
public void executeEvictions(ComponentModel model) {
|
||||||
String parentId = model.getParentId();
|
if (model == null) return;
|
||||||
evictUsers(parentId);
|
// test that this is a realm component
|
||||||
}
|
if (model.getParentId() != null && !model.getParentId().equals(getId())) return;
|
||||||
|
|
||||||
public void evictUsers(String parentId) {
|
// invalidate entire user cache if we're dealing with user storage SPI
|
||||||
if (parentId != null && !parentId.equals(getId())) {
|
if (UserStorageProvider.class.getName().equals(model.getProviderType())) {
|
||||||
ComponentModel parent = getComponent(parentId);
|
session.userCache().evict(this);
|
||||||
if (parent != null && UserStorageProvider.class.getName().equals(parent.getProviderType())) {
|
}
|
||||||
session.userCache().evict(this);
|
// invalidate entire realm if we're dealing with client storage SPI
|
||||||
}
|
// entire realm because of client roles, client lists, and clients
|
||||||
|
if (ClientStorageProvider.class.getName().equals(model.getProviderType())) {
|
||||||
|
cacheSession.evictRealmOnRemoval(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateComponent(ComponentModel component) {
|
public void updateComponent(ComponentModel component) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
evictUsers(component);
|
executeEvictions(component);
|
||||||
updated.updateComponent(component);
|
updated.updateComponent(component);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1359,7 +1361,7 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
@Override
|
@Override
|
||||||
public void removeComponent(ComponentModel component) {
|
public void removeComponent(ComponentModel component) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
evictUsers(component);
|
executeEvictions(component);
|
||||||
updated.removeComponent(component);
|
updated.removeComponent(component);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1367,7 +1369,6 @@ public class RealmAdapter implements CachedRealmModel {
|
||||||
@Override
|
@Override
|
||||||
public void removeComponents(String parentId) {
|
public void removeComponents(String parentId) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
evictUsers(parentId);
|
|
||||||
updated.removeComponents(parentId);
|
updated.removeComponents(parentId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,81 +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.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>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public interface RealmCache {
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
CachedRealm getRealm(String id);
|
|
||||||
|
|
||||||
void invalidateRealm(CachedRealm realm);
|
|
||||||
|
|
||||||
void addRealm(CachedRealm realm);
|
|
||||||
|
|
||||||
CachedRealm getRealmByName(String name);
|
|
||||||
|
|
||||||
void invalidateRealmById(String id);
|
|
||||||
|
|
||||||
CachedClient getClient(String id);
|
|
||||||
|
|
||||||
void invalidateClient(CachedClient app);
|
|
||||||
|
|
||||||
void evictClientById(String id);
|
|
||||||
|
|
||||||
void addClient(CachedClient app);
|
|
||||||
|
|
||||||
void invalidateClientById(String id);
|
|
||||||
|
|
||||||
CachedRole getRole(String id);
|
|
||||||
|
|
||||||
void invalidateRole(CachedRole role);
|
|
||||||
|
|
||||||
void evictRoleById(String id);
|
|
||||||
|
|
||||||
void addRole(CachedRole role);
|
|
||||||
|
|
||||||
void invalidateRoleById(String id);
|
|
||||||
|
|
||||||
CachedGroup getGroup(String id);
|
|
||||||
|
|
||||||
void invalidateGroup(CachedGroup role);
|
|
||||||
|
|
||||||
void addGroup(CachedGroup role);
|
|
||||||
|
|
||||||
void invalidateGroupById(String id);
|
|
||||||
|
|
||||||
CachedClientTemplate getClientTemplate(String id);
|
|
||||||
|
|
||||||
void invalidateClientTemplate(CachedClientTemplate app);
|
|
||||||
|
|
||||||
void evictClientTemplateById(String id);
|
|
||||||
|
|
||||||
void addClientTemplate(CachedClientTemplate app);
|
|
||||||
|
|
||||||
void invalidateClientTemplateById(String id);
|
|
||||||
|
|
||||||
}
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.cluster.ClusterProvider;
|
import org.keycloak.cluster.ClusterProvider;
|
||||||
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.migration.MigrationModel;
|
import org.keycloak.migration.MigrationModel;
|
||||||
import org.keycloak.models.*;
|
import org.keycloak.models.*;
|
||||||
import org.keycloak.models.cache.CacheRealmProvider;
|
import org.keycloak.models.cache.CacheRealmProvider;
|
||||||
|
@ -26,6 +27,9 @@ import org.keycloak.models.cache.CachedRealmModel;
|
||||||
import org.keycloak.models.cache.infinispan.entities.*;
|
import org.keycloak.models.cache.infinispan.entities.*;
|
||||||
import org.keycloak.models.cache.infinispan.events.*;
|
import org.keycloak.models.cache.infinispan.events.*;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||||
|
import org.keycloak.storage.StorageId;
|
||||||
|
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
@ -100,7 +104,7 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
protected boolean setRollbackOnly;
|
protected boolean setRollbackOnly;
|
||||||
|
|
||||||
protected Map<String, RealmAdapter> managedRealms = new HashMap<>();
|
protected Map<String, RealmAdapter> managedRealms = new HashMap<>();
|
||||||
protected Map<String, ClientAdapter> managedApplications = new HashMap<>();
|
protected Map<String, ClientModel> managedApplications = new HashMap<>();
|
||||||
protected Map<String, ClientTemplateAdapter> managedClientTemplates = new HashMap<>();
|
protected Map<String, ClientTemplateAdapter> managedClientTemplates = new HashMap<>();
|
||||||
protected Map<String, RoleAdapter> managedRoles = new HashMap<>();
|
protected Map<String, RoleAdapter> managedRoles = new HashMap<>();
|
||||||
protected Map<String, GroupAdapter> managedGroups = new HashMap<>();
|
protected Map<String, GroupAdapter> managedGroups = new HashMap<>();
|
||||||
|
@ -173,8 +177,8 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
|
|
||||||
private void invalidateClient(String id) {
|
private void invalidateClient(String id) {
|
||||||
invalidations.add(id);
|
invalidations.add(id);
|
||||||
ClientAdapter adapter = managedApplications.get(id);
|
ClientModel adapter = managedApplications.get(id);
|
||||||
if (adapter != null) adapter.invalidate();
|
if (adapter != null && adapter instanceof ClientAdapter) ((ClientAdapter)adapter).invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -204,9 +208,9 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
invalidations.addAll(newInvalidations);
|
invalidations.addAll(newInvalidations);
|
||||||
// need to make sure that scope and group mapping clients and groups are invalidated
|
// need to make sure that scope and group mapping clients and groups are invalidated
|
||||||
for (String id : newInvalidations) {
|
for (String id : newInvalidations) {
|
||||||
ClientAdapter adapter = managedApplications.get(id);
|
ClientModel adapter = managedApplications.get(id);
|
||||||
if (adapter != null) {
|
if (adapter != null && adapter instanceof ClientAdapter){
|
||||||
adapter.invalidate();
|
((ClientAdapter)adapter).invalidate();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
GroupAdapter group = managedGroups.get(id);
|
GroupAdapter group = managedGroups.get(id);
|
||||||
|
@ -329,7 +333,6 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
@Override
|
@Override
|
||||||
public void commit() {
|
public void commit() {
|
||||||
try {
|
try {
|
||||||
if (realmDelegate == null) return;
|
|
||||||
if (clearAll) {
|
if (clearAll) {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
|
@ -470,12 +473,16 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
RealmModel realm = getRealm(id);
|
RealmModel realm = getRealm(id);
|
||||||
if (realm == null) return false;
|
if (realm == null) return false;
|
||||||
|
|
||||||
cache.invalidateObject(id);
|
evictRealmOnRemoval(realm);
|
||||||
invalidationEvents.add(RealmRemovedEvent.create(id, realm.getName()));
|
|
||||||
cache.realmRemoval(id, realm.getName(), invalidations);
|
|
||||||
return getRealmDelegate().removeRealm(id);
|
return getRealmDelegate().removeRealm(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void evictRealmOnRemoval(RealmModel realm) {
|
||||||
|
cache.invalidateObject(realm.getId());
|
||||||
|
invalidationEvents.add(RealmRemovedEvent.create(realm.getId(), realm.getName()));
|
||||||
|
cache.realmRemoval(realm.getId(), realm.getName(), invalidations);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientModel addClient(RealmModel realm, String clientId) {
|
public ClientModel addClient(RealmModel realm, String clientId) {
|
||||||
|
@ -1020,20 +1027,78 @@ public class RealmCacheSession implements CacheRealmProvider {
|
||||||
Long loaded = cache.getCurrentRevision(id);
|
Long loaded = cache.getCurrentRevision(id);
|
||||||
ClientModel model = getClientDelegate().getClientById(id, realm);
|
ClientModel model = getClientDelegate().getClientById(id, realm);
|
||||||
if (model == null) return null;
|
if (model == null) return null;
|
||||||
if (invalidations.contains(id)) return model;
|
ClientModel adapter = cacheClient(realm, model, loaded);
|
||||||
cached = new CachedClient(loaded, realm, model);
|
managedApplications.put(id, adapter);
|
||||||
logger.tracev("adding client by id cache miss: {0}", cached.getClientId());
|
return adapter;
|
||||||
cache.addRevisioned(cached, startupRevision);
|
|
||||||
} else if (invalidations.contains(id)) {
|
} else if (invalidations.contains(id)) {
|
||||||
return getRealmDelegate().getClientById(id, realm);
|
return getRealmDelegate().getClientById(id, realm);
|
||||||
} else if (managedApplications.containsKey(id)) {
|
} else if (managedApplications.containsKey(id)) {
|
||||||
return managedApplications.get(id);
|
return managedApplications.get(id);
|
||||||
}
|
}
|
||||||
ClientAdapter adapter = new ClientAdapter(realm, cached, this, null);
|
ClientModel adapter = validateCache(realm, cached);
|
||||||
managedApplications.put(id, adapter);
|
managedApplications.put(id, adapter);
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ClientModel cacheClient(RealmModel realm, ClientModel delegate, Long revision) {
|
||||||
|
if (invalidations.contains(delegate.getId())) return delegate;
|
||||||
|
StorageId storageId = new StorageId(delegate.getId());
|
||||||
|
CachedClient cached = null;
|
||||||
|
ClientAdapter adapter = null;
|
||||||
|
|
||||||
|
if (!storageId.isLocal()) {
|
||||||
|
ComponentModel component = realm.getComponent(storageId.getProviderId());
|
||||||
|
ClientStorageProviderModel model = new ClientStorageProviderModel(component);
|
||||||
|
if (!model.isEnabled()) {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
ClientStorageProviderModel.CachePolicy policy = model.getCachePolicy();
|
||||||
|
if (policy != null && policy == ClientStorageProviderModel.CachePolicy.NO_CACHE) {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached = new CachedClient(revision, realm, delegate);
|
||||||
|
adapter = new ClientAdapter(realm, cached, this);
|
||||||
|
|
||||||
|
long lifespan = model.getLifespan();
|
||||||
|
if (lifespan > 0) {
|
||||||
|
cache.addRevisioned(cached, startupRevision, lifespan);
|
||||||
|
} else {
|
||||||
|
cache.addRevisioned(cached, startupRevision);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cached = new CachedClient(revision, realm, delegate);
|
||||||
|
adapter = new ClientAdapter(realm, cached, this);
|
||||||
|
cache.addRevisioned(cached, startupRevision);
|
||||||
|
}
|
||||||
|
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected ClientModel validateCache(RealmModel realm, CachedClient cached) {
|
||||||
|
if (!realm.getId().equals(cached.getRealm())) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageId storageId = new StorageId(cached.getId());
|
||||||
|
if (!storageId.isLocal()) {
|
||||||
|
ComponentModel component = realm.getComponent(storageId.getProviderId());
|
||||||
|
ClientStorageProviderModel model = new ClientStorageProviderModel(component);
|
||||||
|
|
||||||
|
// although we do set a timeout, Infinispan has no guarantees when the user will be evicted
|
||||||
|
// its also hard to test stuff
|
||||||
|
if (model.shouldInvalidate(cached)) {
|
||||||
|
registerClientInvalidation(cached.getId(), cached.getClientId(), realm.getId());
|
||||||
|
return getClientDelegate().getClientById(cached.getId(), realm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClientAdapter adapter = new ClientAdapter(realm, cached, this);
|
||||||
|
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
public ClientModel getClientByClientId(String clientId, RealmModel realm) {
|
||||||
String cacheKey = getClientByClientIdCacheKey(clientId, realm.getId());
|
String cacheKey = getClientByClientIdCacheKey(clientId, realm.getId());
|
||||||
|
|
|
@ -19,6 +19,7 @@ package org.keycloak.models.cache.infinispan;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.cluster.ClusterProvider;
|
import org.keycloak.cluster.ClusterProvider;
|
||||||
|
import org.keycloak.models.cache.CachedObject;
|
||||||
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
|
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
|
||||||
import org.keycloak.common.constants.ServiceAccountConstants;
|
import org.keycloak.common.constants.ServiceAccountConstants;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
|
@ -49,6 +50,7 @@ import org.keycloak.models.cache.infinispan.events.UserFederationLinkUpdatedEven
|
||||||
import org.keycloak.models.cache.infinispan.events.UserFullInvalidationEvent;
|
import org.keycloak.models.cache.infinispan.events.UserFullInvalidationEvent;
|
||||||
import org.keycloak.models.cache.infinispan.events.UserUpdatedEvent;
|
import org.keycloak.models.cache.infinispan.events.UserUpdatedEvent;
|
||||||
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
import org.keycloak.models.utils.ReadOnlyUserModelDelegate;
|
||||||
|
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||||
import org.keycloak.storage.StorageId;
|
import org.keycloak.storage.StorageId;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.UserStorageProviderModel;
|
import org.keycloak.storage.UserStorageProviderModel;
|
||||||
|
@ -144,7 +146,6 @@ public class UserCacheSession implements UserCache {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void commit() {
|
public void commit() {
|
||||||
if (delegate == null) return;
|
|
||||||
runInvalidations();
|
runInvalidations();
|
||||||
transactionActive = false;
|
transactionActive = false;
|
||||||
}
|
}
|
||||||
|
@ -296,46 +297,11 @@ public class UserCacheSession implements UserCache {
|
||||||
|
|
||||||
if (!storageId.isLocal()) {
|
if (!storageId.isLocal()) {
|
||||||
ComponentModel component = realm.getComponent(storageId.getProviderId());
|
ComponentModel component = realm.getComponent(storageId.getProviderId());
|
||||||
UserStorageProviderModel model = new UserStorageProviderModel(component);
|
CacheableStorageProviderModel model = new CacheableStorageProviderModel(component);
|
||||||
|
|
||||||
// although we do set a timeout, Infinispan has no guarantees when the user will be evicted
|
// although we do set a timeout, Infinispan has no guarantees when the user will be evicted
|
||||||
// its also hard to test stuff
|
// its also hard to test stuff
|
||||||
boolean invalidate = false;
|
if (model.shouldInvalidate(cached)) {
|
||||||
if (!model.isEnabled()) {
|
|
||||||
invalidate = true;
|
|
||||||
} else {
|
|
||||||
UserStorageProviderModel.CachePolicy policy = model.getCachePolicy();
|
|
||||||
if (policy != null) {
|
|
||||||
//String currentTime = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(Time.currentTimeMillis()));
|
|
||||||
if (policy == UserStorageProviderModel.CachePolicy.NO_CACHE) {
|
|
||||||
invalidate = true;
|
|
||||||
} else if (cached.getCacheTimestamp() < model.getCacheInvalidBefore()) {
|
|
||||||
invalidate = true;
|
|
||||||
} else if (policy == UserStorageProviderModel.CachePolicy.MAX_LIFESPAN) {
|
|
||||||
if (cached.getCacheTimestamp() + model.getMaxLifespan() < Time.currentTimeMillis()) {
|
|
||||||
invalidate = true;
|
|
||||||
}
|
|
||||||
} else if (policy == UserStorageProviderModel.CachePolicy.EVICT_DAILY) {
|
|
||||||
long dailyTimeout = dailyTimeout(model.getEvictionHour(), model.getEvictionMinute());
|
|
||||||
dailyTimeout = dailyTimeout - (24 * 60 * 60 * 1000);
|
|
||||||
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(dailyTimeout));
|
|
||||||
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
|
||||||
if (cached.getCacheTimestamp() <= dailyTimeout) {
|
|
||||||
invalidate = true;
|
|
||||||
}
|
|
||||||
} else if (policy == UserStorageProviderModel.CachePolicy.EVICT_WEEKLY) {
|
|
||||||
int oneWeek = 7 * 24 * 60 * 60 * 1000;
|
|
||||||
long weeklyTimeout = weeklyTimeout(model.getEvictionDay(), model.getEvictionHour(), model.getEvictionMinute());
|
|
||||||
long lastTimeout = weeklyTimeout - oneWeek;
|
|
||||||
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(weeklyTimeout));
|
|
||||||
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
|
||||||
if (cached.getCacheTimestamp() <= lastTimeout) {
|
|
||||||
invalidate = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (invalidate) {
|
|
||||||
registerUserInvalidation(realm, cached);
|
registerUserInvalidation(realm, cached);
|
||||||
return getDelegate().getUserById(cached.getId(), realm);
|
return getDelegate().getUserById(cached.getId(), realm);
|
||||||
}
|
}
|
||||||
|
@ -371,26 +337,11 @@ public class UserCacheSession implements UserCache {
|
||||||
adapter = new UserAdapter(cached, this, session, realm);
|
adapter = new UserAdapter(cached, this, session, realm);
|
||||||
onCache(realm, adapter, delegate);
|
onCache(realm, adapter, delegate);
|
||||||
|
|
||||||
if (policy == null || policy == UserStorageProviderModel.CachePolicy.DEFAULT) {
|
long lifespan = model.getLifespan();
|
||||||
cache.addRevisioned(cached, startupRevision);
|
if (lifespan > 0) {
|
||||||
|
cache.addRevisioned(cached, startupRevision, lifespan);
|
||||||
} else {
|
} else {
|
||||||
long lifespan = -1;
|
cache.addRevisioned(cached, startupRevision);
|
||||||
if (policy == UserStorageProviderModel.CachePolicy.EVICT_DAILY) {
|
|
||||||
if (model.getEvictionHour() > -1 && model.getEvictionMinute() > -1) {
|
|
||||||
lifespan = dailyTimeout(model.getEvictionHour(), model.getEvictionMinute()) - Time.currentTimeMillis();
|
|
||||||
}
|
|
||||||
} else if (policy == UserStorageProviderModel.CachePolicy.EVICT_WEEKLY) {
|
|
||||||
if (model.getEvictionDay() > 0 && model.getEvictionHour() > -1 && model.getEvictionMinute() > -1) {
|
|
||||||
lifespan = weeklyTimeout(model.getEvictionDay(), model.getEvictionHour(), model.getEvictionMinute()) - Time.currentTimeMillis();
|
|
||||||
}
|
|
||||||
} else if (policy == UserStorageProviderModel.CachePolicy.MAX_LIFESPAN) {
|
|
||||||
lifespan = model.getMaxLifespan();
|
|
||||||
}
|
|
||||||
if (lifespan > 0) {
|
|
||||||
cache.addRevisioned(cached, startupRevision, lifespan);
|
|
||||||
} else {
|
|
||||||
cache.addRevisioned(cached, startupRevision);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cached = new CachedUser(revision, realm, delegate, notBefore);
|
cached = new CachedUser(revision, realm, delegate, notBefore);
|
||||||
|
@ -402,39 +353,6 @@ public class UserCacheSession implements UserCache {
|
||||||
return adapter;
|
return adapter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static long dailyTimeout(int hour, int minute) {
|
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
Calendar cal2 = Calendar.getInstance();
|
|
||||||
cal.setTimeInMillis(Time.currentTimeMillis());
|
|
||||||
cal2.setTimeInMillis(Time.currentTimeMillis());
|
|
||||||
cal2.set(Calendar.HOUR_OF_DAY, hour);
|
|
||||||
cal2.set(Calendar.MINUTE, minute);
|
|
||||||
if (cal2.getTimeInMillis() < cal.getTimeInMillis()) {
|
|
||||||
int add = (24 * 60 * 60 * 1000);
|
|
||||||
cal.add(Calendar.MILLISECOND, add);
|
|
||||||
} else {
|
|
||||||
cal.add(Calendar.MILLISECOND, (int)(cal2.getTimeInMillis() - cal.getTimeInMillis()));
|
|
||||||
}
|
|
||||||
return cal.getTimeInMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long weeklyTimeout(int day, int hour, int minute) {
|
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
Calendar cal2 = Calendar.getInstance();
|
|
||||||
cal.setTimeInMillis(Time.currentTimeMillis());
|
|
||||||
cal2.setTimeInMillis(Time.currentTimeMillis());
|
|
||||||
cal2.set(Calendar.HOUR_OF_DAY, hour);
|
|
||||||
cal2.set(Calendar.MINUTE, minute);
|
|
||||||
cal2.set(Calendar.DAY_OF_WEEK, day);
|
|
||||||
if (cal2.getTimeInMillis() < cal.getTimeInMillis()) {
|
|
||||||
int add = (7 * 24 * 60 * 60 * 1000);
|
|
||||||
cal2.add(Calendar.MILLISECOND, add);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cal2.getTimeInMillis();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onCache(RealmModel realm, UserAdapter adapter, UserModel delegate) {
|
private void onCache(RealmModel realm, UserAdapter adapter, UserModel delegate) {
|
||||||
((OnUserCache)getDelegate()).onCache(realm, adapter, delegate);
|
((OnUserCache)getDelegate()).onCache(realm, adapter, delegate);
|
||||||
((OnUserCache)session.userCredentialManager()).onCache(realm, adapter, delegate);
|
((OnUserCache)session.userCredentialManager()).onCache(realm, adapter, delegate);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package org.keycloak.models.cache.infinispan.entities;
|
package org.keycloak.models.cache.infinispan.entities;
|
||||||
|
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
|
import org.keycloak.models.cache.CachedObject;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@ -8,7 +9,7 @@ import java.io.Serializable;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class AbstractRevisioned implements Revisioned, Serializable {
|
public class AbstractRevisioned implements Revisioned, Serializable, CachedObject {
|
||||||
private String id;
|
private String id;
|
||||||
private Long revision;
|
private Long revision;
|
||||||
private final long cacheTimestamp = Time.currentTimeMillis();
|
private final long cacheTimestamp = Time.currentTimeMillis();
|
||||||
|
@ -38,6 +39,7 @@ public class AbstractRevisioned implements Revisioned, Serializable {
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public long getCacheTimestamp() {
|
public long getCacheTimestamp() {
|
||||||
return cacheTimestamp;
|
return cacheTimestamp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package org.keycloak.models.sessions.infinispan.initializer;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.models.cache.infinispan.UserCacheSession;
|
import org.keycloak.models.cache.infinispan.UserCacheSession;
|
||||||
|
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
@ -65,13 +66,13 @@ public class InitializerStateTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDailyTimeout() throws Exception {
|
public void testDailyTimeout() throws Exception {
|
||||||
Date date = new Date(UserCacheSession.dailyTimeout(10, 30));
|
Date date = new Date(CacheableStorageProviderModel.dailyTimeout(10, 30));
|
||||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||||
date = new Date(UserCacheSession.dailyTimeout(17, 45));
|
date = new Date(CacheableStorageProviderModel.dailyTimeout(17, 45));
|
||||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||||
date = new Date(UserCacheSession.weeklyTimeout(Calendar.MONDAY, 13, 45));
|
date = new Date(CacheableStorageProviderModel.weeklyTimeout(Calendar.MONDAY, 13, 45));
|
||||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||||
date = new Date(UserCacheSession.weeklyTimeout(Calendar.THURSDAY, 13, 45));
|
date = new Date(CacheableStorageProviderModel.weeklyTimeout(Calendar.THURSDAY, 13, 45));
|
||||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||||
System.out.println("----");
|
System.out.println("----");
|
||||||
Calendar cal = Calendar.getInstance();
|
Calendar cal = Calendar.getInstance();
|
||||||
|
@ -80,7 +81,7 @@ public class InitializerStateTest {
|
||||||
int min = cal.get(Calendar.MINUTE);
|
int min = cal.get(Calendar.MINUTE);
|
||||||
date = new Date(cal.getTimeInMillis());
|
date = new Date(cal.getTimeInMillis());
|
||||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||||
date = new Date(UserCacheSession.dailyTimeout(hour, min));
|
date = new Date(CacheableStorageProviderModel.dailyTimeout(hour, min));
|
||||||
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
System.out.println(DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(date));
|
||||||
cal = Calendar.getInstance();
|
cal = Calendar.getInstance();
|
||||||
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
|
||||||
|
|
25
server-spi/src/main/java/org/keycloak/models/cache/CachedObject.java
vendored
Normal file
25
server-spi/src/main/java/org/keycloak/models/cache/CachedObject.java
vendored
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface CachedObject {
|
||||||
|
long getCacheTimestamp();
|
||||||
|
}
|
|
@ -16,8 +16,12 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.storage;
|
package org.keycloak.storage;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.component.PrioritizedComponentModel;
|
import org.keycloak.component.PrioritizedComponentModel;
|
||||||
|
import org.keycloak.models.cache.CachedObject;
|
||||||
|
|
||||||
|
import java.util.Calendar;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
@ -30,6 +34,7 @@ public class CacheableStorageProviderModel extends PrioritizedComponentModel {
|
||||||
public static final String EVICTION_MINUTE = "evictionMinute";
|
public static final String EVICTION_MINUTE = "evictionMinute";
|
||||||
public static final String EVICTION_DAY = "evictionDay";
|
public static final String EVICTION_DAY = "evictionDay";
|
||||||
public static final String CACHE_INVALID_BEFORE = "cacheInvalidBefore";
|
public static final String CACHE_INVALID_BEFORE = "cacheInvalidBefore";
|
||||||
|
public static final String ENABLED = "enabled";
|
||||||
|
|
||||||
private transient CachePolicy cachePolicy;
|
private transient CachePolicy cachePolicy;
|
||||||
private transient long maxLifespan = -1;
|
private transient long maxLifespan = -1;
|
||||||
|
@ -37,6 +42,7 @@ public class CacheableStorageProviderModel extends PrioritizedComponentModel {
|
||||||
private transient int evictionMinute = -1;
|
private transient int evictionMinute = -1;
|
||||||
private transient int evictionDay = -1;
|
private transient int evictionDay = -1;
|
||||||
private transient long cacheInvalidBefore = -1;
|
private transient long cacheInvalidBefore = -1;
|
||||||
|
private transient Boolean enabled;
|
||||||
|
|
||||||
public CacheableStorageProviderModel() {
|
public CacheableStorageProviderModel() {
|
||||||
}
|
}
|
||||||
|
@ -137,6 +143,117 @@ public class CacheableStorageProviderModel extends PrioritizedComponentModel {
|
||||||
getConfig().putSingle(CACHE_INVALID_BEFORE, Long.toString(cacheInvalidBefore));
|
getConfig().putSingle(CACHE_INVALID_BEFORE, Long.toString(cacheInvalidBefore));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEnabled(boolean flag) {
|
||||||
|
enabled = flag;
|
||||||
|
getConfig().putSingle(ENABLED, Boolean.toString(flag));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnabled() {
|
||||||
|
if (enabled == null) {
|
||||||
|
String val = getConfig().getFirst(ENABLED);
|
||||||
|
if (val == null) {
|
||||||
|
enabled = true;
|
||||||
|
} else {
|
||||||
|
enabled = Boolean.valueOf(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return enabled;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLifespan() {
|
||||||
|
UserStorageProviderModel.CachePolicy policy = getCachePolicy();
|
||||||
|
long lifespan = -1;
|
||||||
|
if (policy == null || policy == UserStorageProviderModel.CachePolicy.DEFAULT) {
|
||||||
|
lifespan = -1;
|
||||||
|
} else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_DAILY) {
|
||||||
|
if (getEvictionHour() > -1 && getEvictionMinute() > -1) {
|
||||||
|
lifespan = dailyTimeout(getEvictionHour(), getEvictionMinute()) - Time.currentTimeMillis();
|
||||||
|
}
|
||||||
|
} else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_WEEKLY) {
|
||||||
|
if (getEvictionDay() > 0 && getEvictionHour() > -1 && getEvictionMinute() > -1) {
|
||||||
|
lifespan = weeklyTimeout(getEvictionDay(), getEvictionHour(), getEvictionMinute()) - Time.currentTimeMillis();
|
||||||
|
}
|
||||||
|
} else if (policy == CacheableStorageProviderModel.CachePolicy.MAX_LIFESPAN) {
|
||||||
|
lifespan = getMaxLifespan();
|
||||||
|
}
|
||||||
|
return lifespan;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean shouldInvalidate(CachedObject cached) {
|
||||||
|
boolean invalidate = false;
|
||||||
|
if (!isEnabled()) {
|
||||||
|
invalidate = true;
|
||||||
|
} else {
|
||||||
|
CacheableStorageProviderModel.CachePolicy policy = getCachePolicy();
|
||||||
|
if (policy != null) {
|
||||||
|
//String currentTime = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(Time.currentTimeMillis()));
|
||||||
|
if (policy == CacheableStorageProviderModel.CachePolicy.NO_CACHE) {
|
||||||
|
invalidate = true;
|
||||||
|
} else if (cached.getCacheTimestamp() < getCacheInvalidBefore()) {
|
||||||
|
invalidate = true;
|
||||||
|
} else if (policy == CacheableStorageProviderModel.CachePolicy.MAX_LIFESPAN) {
|
||||||
|
if (cached.getCacheTimestamp() + getMaxLifespan() < Time.currentTimeMillis()) {
|
||||||
|
invalidate = true;
|
||||||
|
}
|
||||||
|
} else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_DAILY) {
|
||||||
|
long dailyTimeout = dailyTimeout(getEvictionHour(), getEvictionMinute());
|
||||||
|
dailyTimeout = dailyTimeout - (24 * 60 * 60 * 1000);
|
||||||
|
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(dailyTimeout));
|
||||||
|
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
||||||
|
if (cached.getCacheTimestamp() <= dailyTimeout) {
|
||||||
|
invalidate = true;
|
||||||
|
}
|
||||||
|
} else if (policy == CacheableStorageProviderModel.CachePolicy.EVICT_WEEKLY) {
|
||||||
|
int oneWeek = 7 * 24 * 60 * 60 * 1000;
|
||||||
|
long weeklyTimeout = weeklyTimeout(getEvictionDay(), getEvictionHour(), getEvictionMinute());
|
||||||
|
long lastTimeout = weeklyTimeout - oneWeek;
|
||||||
|
//String timeout = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(weeklyTimeout));
|
||||||
|
//String stamp = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL).format(new Date(cached.getCacheTimestamp()));
|
||||||
|
if (cached.getCacheTimestamp() <= lastTimeout) {
|
||||||
|
invalidate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return invalidate;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static long dailyTimeout(int hour, int minute) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
Calendar cal2 = Calendar.getInstance();
|
||||||
|
cal.setTimeInMillis(Time.currentTimeMillis());
|
||||||
|
cal2.setTimeInMillis(Time.currentTimeMillis());
|
||||||
|
cal2.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
cal2.set(Calendar.MINUTE, minute);
|
||||||
|
if (cal2.getTimeInMillis() < cal.getTimeInMillis()) {
|
||||||
|
int add = (24 * 60 * 60 * 1000);
|
||||||
|
cal.add(Calendar.MILLISECOND, add);
|
||||||
|
} else {
|
||||||
|
cal.add(Calendar.MILLISECOND, (int)(cal2.getTimeInMillis() - cal.getTimeInMillis()));
|
||||||
|
}
|
||||||
|
return cal.getTimeInMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long weeklyTimeout(int day, int hour, int minute) {
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
Calendar cal2 = Calendar.getInstance();
|
||||||
|
cal.setTimeInMillis(Time.currentTimeMillis());
|
||||||
|
cal2.setTimeInMillis(Time.currentTimeMillis());
|
||||||
|
cal2.set(Calendar.HOUR_OF_DAY, hour);
|
||||||
|
cal2.set(Calendar.MINUTE, minute);
|
||||||
|
cal2.set(Calendar.DAY_OF_WEEK, day);
|
||||||
|
if (cal2.getTimeInMillis() < cal.getTimeInMillis()) {
|
||||||
|
int add = (7 * 24 * 60 * 60 * 1000);
|
||||||
|
cal2.add(Calendar.MILLISECOND, add);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cal2.getTimeInMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public enum CachePolicy {
|
public enum CachePolicy {
|
||||||
NO_CACHE,
|
NO_CACHE,
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
|
|
|
@ -18,7 +18,6 @@
|
||||||
package org.keycloak.storage;
|
package org.keycloak.storage;
|
||||||
|
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.component.PrioritizedComponentModel;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stored configuration of a User Storage provider instance.
|
* Stored configuration of a User Storage provider instance.
|
||||||
|
@ -32,7 +31,6 @@ public class UserStorageProviderModel extends CacheableStorageProviderModel {
|
||||||
public static final String FULL_SYNC_PERIOD = "fullSyncPeriod";
|
public static final String FULL_SYNC_PERIOD = "fullSyncPeriod";
|
||||||
public static final String CHANGED_SYNC_PERIOD = "changedSyncPeriod";
|
public static final String CHANGED_SYNC_PERIOD = "changedSyncPeriod";
|
||||||
public static final String LAST_SYNC = "lastSync";
|
public static final String LAST_SYNC = "lastSync";
|
||||||
public static final String ENABLED = "enabled";
|
|
||||||
|
|
||||||
public UserStorageProviderModel() {
|
public UserStorageProviderModel() {
|
||||||
setProviderType(UserStorageProvider.class.getName());
|
setProviderType(UserStorageProvider.class.getName());
|
||||||
|
@ -46,7 +44,6 @@ public class UserStorageProviderModel extends CacheableStorageProviderModel {
|
||||||
private transient Integer changedSyncPeriod;
|
private transient Integer changedSyncPeriod;
|
||||||
private transient Integer lastSync;
|
private transient Integer lastSync;
|
||||||
private transient Boolean importEnabled;
|
private transient Boolean importEnabled;
|
||||||
private transient Boolean enabled;
|
|
||||||
|
|
||||||
public boolean isImportEnabled() {
|
public boolean isImportEnabled() {
|
||||||
if (importEnabled == null) {
|
if (importEnabled == null) {
|
||||||
|
@ -66,24 +63,6 @@ public class UserStorageProviderModel extends CacheableStorageProviderModel {
|
||||||
getConfig().putSingle(IMPORT_ENABLED, Boolean.toString(flag));
|
getConfig().putSingle(IMPORT_ENABLED, Boolean.toString(flag));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean flag) {
|
|
||||||
enabled = flag;
|
|
||||||
getConfig().putSingle(ENABLED, Boolean.toString(flag));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
if (enabled == null) {
|
|
||||||
String val = getConfig().getFirst(ENABLED);
|
|
||||||
if (val == null) {
|
|
||||||
enabled = true;
|
|
||||||
} else {
|
|
||||||
enabled = Boolean.valueOf(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return enabled;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getFullSyncPeriod() {
|
public int getFullSyncPeriod() {
|
||||||
if (fullSyncPeriod == null) {
|
if (fullSyncPeriod == null) {
|
||||||
|
|
|
@ -30,17 +30,21 @@ import org.keycloak.admin.client.resource.ClientsResource;
|
||||||
import org.keycloak.admin.client.resource.UserResource;
|
import org.keycloak.admin.client.resource.UserResource;
|
||||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||||
import org.keycloak.common.util.MultivaluedHashMap;
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.AuthenticationFlowBindings;
|
import org.keycloak.models.AuthenticationFlowBindings;
|
||||||
import org.keycloak.models.AuthenticationFlowModel;
|
import org.keycloak.models.AuthenticationFlowModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.cache.infinispan.ClientAdapter;
|
||||||
import org.keycloak.representations.idm.ClientRepresentation;
|
import org.keycloak.representations.idm.ClientRepresentation;
|
||||||
import org.keycloak.representations.idm.ComponentRepresentation;
|
import org.keycloak.representations.idm.ComponentRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
|
import org.keycloak.storage.CacheableStorageProviderModel;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.client.ClientStorageProvider;
|
import org.keycloak.storage.client.ClientStorageProvider;
|
||||||
|
import org.keycloak.storage.client.ClientStorageProviderModel;
|
||||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
|
@ -66,9 +70,18 @@ import javax.ws.rs.core.Response;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.Calendar.DAY_OF_WEEK;
|
||||||
|
import static java.util.Calendar.HOUR_OF_DAY;
|
||||||
|
import static java.util.Calendar.MINUTE;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.keycloak.storage.CacheableStorageProviderModel.CACHE_POLICY;
|
||||||
|
import static org.keycloak.storage.CacheableStorageProviderModel.EVICTION_DAY;
|
||||||
|
import static org.keycloak.storage.CacheableStorageProviderModel.EVICTION_HOUR;
|
||||||
|
import static org.keycloak.storage.CacheableStorageProviderModel.EVICTION_MINUTE;
|
||||||
|
import static org.keycloak.storage.CacheableStorageProviderModel.MAX_LIFESPAN;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that clients can override auth flows
|
* Test that clients can override auth flows
|
||||||
|
@ -92,6 +105,8 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest {
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String providerId;
|
||||||
|
|
||||||
@Deployment
|
@Deployment
|
||||||
public static WebArchive deploy() {
|
public static WebArchive deploy() {
|
||||||
return RunOnServerDeployment.create(UserResource.class)
|
return RunOnServerDeployment.create(UserResource.class)
|
||||||
|
@ -116,7 +131,7 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest {
|
||||||
provider.getConfig().putSingle(HardcodedClientStorageProviderFactory.CLIENT_ID, "hardcoded-client");
|
provider.getConfig().putSingle(HardcodedClientStorageProviderFactory.CLIENT_ID, "hardcoded-client");
|
||||||
provider.getConfig().putSingle(HardcodedClientStorageProviderFactory.REDIRECT_URI, oauth.getRedirectUri());
|
provider.getConfig().putSingle(HardcodedClientStorageProviderFactory.REDIRECT_URI, oauth.getRedirectUri());
|
||||||
|
|
||||||
String providerId = addComponent(provider);
|
providerId = addComponent(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -212,4 +227,176 @@ public class ClientStorageTest extends AbstractTestRealmKeycloakTest {
|
||||||
httpClient.close();
|
httpClient.close();
|
||||||
events.clear();
|
events.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDailyEviction() {
|
||||||
|
|
||||||
|
// set eviction to 1 hour from now
|
||||||
|
Calendar eviction = Calendar.getInstance();
|
||||||
|
eviction.add(Calendar.HOUR, 1);
|
||||||
|
ComponentRepresentation propProviderRW = testRealmResource().components().component(propProviderRWId).toRepresentation();
|
||||||
|
propProviderRW.getConfig().putSingle(CACHE_POLICY, CacheableStorageProviderModel.CachePolicy.EVICT_DAILY.name());
|
||||||
|
propProviderRW.getConfig().putSingle(EVICTION_HOUR, Integer.toString(eviction.get(HOUR_OF_DAY)));
|
||||||
|
propProviderRW.getConfig().putSingle(EVICTION_MINUTE, Integer.toString(eviction.get(MINUTE)));
|
||||||
|
testRealmResource().components().component(propProviderRWId).update(propProviderRW);
|
||||||
|
|
||||||
|
// now
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
UserModel user = session.users().getUserByUsername("thor", realm);
|
||||||
|
});
|
||||||
|
|
||||||
|
// run twice to make sure its in cache.
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
UserModel user = session.users().getUserByUsername("thor", realm);
|
||||||
|
System.out.println("User class: " + user.getClass());
|
||||||
|
Assert.assertTrue(user instanceof CachedUserModel); // should still be cached
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeOffset(2 * 60 * 60); // 2 hours in future
|
||||||
|
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
UserModel user = session.users().getUserByUsername("thor", realm);
|
||||||
|
System.out.println("User class: " + user.getClass());
|
||||||
|
Assert.assertFalse(user instanceof CachedUserModel); // should be evicted
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDailyEviction() {
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||||
|
Calendar eviction = Calendar.getInstance();
|
||||||
|
eviction.add(Calendar.HOUR, 1);
|
||||||
|
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.EVICT_DAILY);
|
||||||
|
model.setEvictionHour(eviction.get(HOUR_OF_DAY));
|
||||||
|
model.setEvictionMinute(eviction.get(MINUTE));
|
||||||
|
realm.updateComponent(model);
|
||||||
|
});
|
||||||
|
testIsCached();
|
||||||
|
setTimeOffset(2 * 60 * 60); // 2 hours in future
|
||||||
|
testNotCached();
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
setDefaultCachePolicy();
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testWeeklyEviction() {
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||||
|
Calendar eviction = Calendar.getInstance();
|
||||||
|
eviction.add(Calendar.HOUR, 4 * 24);
|
||||||
|
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.EVICT_WEEKLY);
|
||||||
|
model.setEvictionDay(eviction.get(DAY_OF_WEEK));
|
||||||
|
model.setEvictionHour(eviction.get(HOUR_OF_DAY));
|
||||||
|
model.setEvictionMinute(eviction.get(MINUTE));
|
||||||
|
realm.updateComponent(model);
|
||||||
|
});
|
||||||
|
testIsCached();
|
||||||
|
setTimeOffset(2 * 24 * 60 * 60); // 2 days in future
|
||||||
|
testIsCached();
|
||||||
|
setTimeOffset(5 * 24 * 60 * 60); // 5 days in future
|
||||||
|
testNotCached();
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
setDefaultCachePolicy();
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testMaxLifespan() {
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||||
|
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.MAX_LIFESPAN);
|
||||||
|
model.setMaxLifespan(1 * 60 * 60 * 1000);
|
||||||
|
realm.updateComponent(model);
|
||||||
|
});
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
setTimeOffset(1/2 * 60 * 60); // 1/2 hour in future
|
||||||
|
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
setTimeOffset(2 * 60 * 60); // 2 hours in future
|
||||||
|
|
||||||
|
testNotCached();
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
setDefaultCachePolicy();
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testNotCached() {
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
ClientModel hardcoded = realm.getClientByClientId("hardcoded-client");
|
||||||
|
Assert.assertNotNull(hardcoded);
|
||||||
|
Assert.assertFalse(hardcoded instanceof ClientAdapter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIsCached() {
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
ClientModel hardcoded = realm.getClientByClientId("hardcoded-client");
|
||||||
|
Assert.assertNotNull(hardcoded);
|
||||||
|
Assert.assertTrue(hardcoded instanceof org.keycloak.models.cache.infinispan.ClientAdapter);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoCache() {
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||||
|
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.NO_CACHE);
|
||||||
|
realm.updateComponent(model);
|
||||||
|
});
|
||||||
|
|
||||||
|
testNotCached();
|
||||||
|
|
||||||
|
// test twice because updating component should evict
|
||||||
|
testNotCached();
|
||||||
|
|
||||||
|
// set it back
|
||||||
|
setDefaultCachePolicy();
|
||||||
|
testIsCached();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDefaultCachePolicy() {
|
||||||
|
testingClient.server().run(session -> {
|
||||||
|
RealmModel realm = session.realms().getRealmByName("test");
|
||||||
|
ClientStorageProviderModel model = realm.getClientStorageProviders().get(0);
|
||||||
|
model.setCachePolicy(CacheableStorageProviderModel.CachePolicy.DEFAULT);
|
||||||
|
realm.updateComponent(model);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue