Split PublicKeyStorageProvider (#12897)
Split PublicKeyStorageProvider - Extract clearCache() method to separate interface and move it to the legacy module - Make PublicKeyProvider factories environment dependent - Simple map storage for public keys that just delegates Resolves #12763 Co-authored-by: Martin Kanis <mkanis@redhat.com>
This commit is contained in:
parent
63614b1240
commit
098d4dda0e
29 changed files with 668 additions and 56 deletions
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.keys.infinispan;
|
||||||
|
|
||||||
|
import org.infinispan.Cache;
|
||||||
|
import org.keycloak.cluster.ClusterProvider;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.cache.CachePublicKeyProvider;
|
||||||
|
import org.keycloak.models.cache.infinispan.ClearCacheEvent;
|
||||||
|
|
||||||
|
public class InfinispanCachePublicKeyProvider implements CachePublicKeyProvider {
|
||||||
|
|
||||||
|
private final KeycloakSession session;
|
||||||
|
|
||||||
|
private final Cache<String, PublicKeysEntry> keys;
|
||||||
|
|
||||||
|
public InfinispanCachePublicKeyProvider(KeycloakSession session, Cache<String, PublicKeysEntry> keys) {
|
||||||
|
this.session = session;
|
||||||
|
this.keys = keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearCache() {
|
||||||
|
keys.clear();
|
||||||
|
ClusterProvider cluster = session.getProvider(ClusterProvider.class);
|
||||||
|
cluster.notify(InfinispanCachePublicKeyProviderFactory.KEYS_CLEAR_CACHE_EVENTS, new ClearCacheEvent(), true, ClusterProvider.DCNotify.ALL_DCS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.keys.infinispan;
|
||||||
|
|
||||||
|
import org.infinispan.Cache;
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.cluster.ClusterEvent;
|
||||||
|
import org.keycloak.cluster.ClusterProvider;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.cache.CachePublicKeyProvider;
|
||||||
|
import org.keycloak.models.cache.CachePublicKeyProviderFactory;
|
||||||
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
|
|
||||||
|
public class InfinispanCachePublicKeyProviderFactory implements CachePublicKeyProviderFactory, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
|
public static final String PROVIDER_ID = "infinispan";
|
||||||
|
|
||||||
|
public static final String PUBLIC_KEY_STORAGE_INVALIDATION_EVENT = "PUBLIC_KEY_STORAGE_INVALIDATION_EVENT";
|
||||||
|
|
||||||
|
public static final String KEYS_CLEAR_CACHE_EVENTS = "KEYS_CLEAR_CACHE_EVENTS";
|
||||||
|
|
||||||
|
private volatile Cache<String, PublicKeysEntry> keysCache;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CachePublicKeyProvider create(KeycloakSession session) {
|
||||||
|
lazyInit(session);
|
||||||
|
return new InfinispanCachePublicKeyProvider(session, keysCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void lazyInit(KeycloakSession session) {
|
||||||
|
if (keysCache == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (keysCache == null) {
|
||||||
|
this.keysCache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME);
|
||||||
|
|
||||||
|
ClusterProvider cluster = session.getProvider(ClusterProvider.class);
|
||||||
|
cluster.registerListener(PUBLIC_KEY_STORAGE_INVALIDATION_EVENT, (ClusterEvent event) -> {
|
||||||
|
|
||||||
|
PublicKeyStorageInvalidationEvent invalidationEvent = (PublicKeyStorageInvalidationEvent) event;
|
||||||
|
keysCache.remove(invalidationEvent.getCacheKey());
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
cluster.registerListener(KEYS_CLEAR_CACHE_EVENTS, (ClusterEvent event) -> {
|
||||||
|
|
||||||
|
keysCache.clear();
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported() {
|
||||||
|
return !Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return PROVIDER_ID;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,7 +34,6 @@ import org.keycloak.keys.PublicKeyLoader;
|
||||||
import org.keycloak.keys.PublicKeyStorageProvider;
|
import org.keycloak.keys.PublicKeyStorageProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakTransaction;
|
import org.keycloak.models.KeycloakTransaction;
|
||||||
import org.keycloak.models.cache.infinispan.ClearCacheEvent;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,15 +62,6 @@ public class InfinispanPublicKeyStorageProvider implements PublicKeyStorageProvi
|
||||||
this.minTimeBetweenRequests = minTimeBetweenRequests;
|
this.minTimeBetweenRequests = minTimeBetweenRequests;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearCache() {
|
|
||||||
keys.clear();
|
|
||||||
ClusterProvider cluster = session.getProvider(ClusterProvider.class);
|
|
||||||
cluster.notify(InfinispanPublicKeyStorageProviderFactory.KEYS_CLEAR_CACHE_EVENTS, new ClearCacheEvent(), true, ClusterProvider.DCNotify.ALL_DCS);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void addInvalidation(String cacheKey) {
|
void addInvalidation(String cacheKey) {
|
||||||
if (!transactionEnlisted) {
|
if (!transactionEnlisted) {
|
||||||
session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
|
session.getTransactionManager().enlistAfterCompletion(getAfterTransaction());
|
||||||
|
@ -121,7 +111,7 @@ public class InfinispanPublicKeyStorageProvider implements PublicKeyStorageProvi
|
||||||
|
|
||||||
for (String cacheKey : invalidations) {
|
for (String cacheKey : invalidations) {
|
||||||
keys.remove(cacheKey);
|
keys.remove(cacheKey);
|
||||||
cluster.notify(InfinispanPublicKeyStorageProviderFactory.PUBLIC_KEY_STORAGE_INVALIDATION_EVENT, PublicKeyStorageInvalidationEvent.create(cacheKey), true, ClusterProvider.DCNotify.ALL_DCS);
|
cluster.notify(InfinispanCachePublicKeyProviderFactory.PUBLIC_KEY_STORAGE_INVALIDATION_EVENT, PublicKeyStorageInvalidationEvent.create(cacheKey), true, ClusterProvider.DCNotify.ALL_DCS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,7 @@ import java.util.concurrent.FutureTask;
|
||||||
import org.infinispan.Cache;
|
import org.infinispan.Cache;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.cluster.ClusterEvent;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.cluster.ClusterProvider;
|
|
||||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||||
import org.keycloak.jose.jwk.JWK;
|
import org.keycloak.jose.jwk.JWK;
|
||||||
import org.keycloak.keys.PublicKeyStorageProvider;
|
import org.keycloak.keys.PublicKeyStorageProvider;
|
||||||
|
@ -36,22 +35,19 @@ import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
import org.keycloak.provider.ProviderEvent;
|
import org.keycloak.provider.ProviderEvent;
|
||||||
import org.keycloak.provider.ProviderEventListener;
|
import org.keycloak.provider.ProviderEventListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public class InfinispanPublicKeyStorageProviderFactory implements PublicKeyStorageProviderFactory {
|
public class InfinispanPublicKeyStorageProviderFactory implements PublicKeyStorageProviderFactory, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(InfinispanPublicKeyStorageProviderFactory.class);
|
private static final Logger log = Logger.getLogger(InfinispanPublicKeyStorageProviderFactory.class);
|
||||||
|
|
||||||
public static final String PROVIDER_ID = "infinispan";
|
public static final String PROVIDER_ID = "infinispan";
|
||||||
|
|
||||||
public static final String KEYS_CLEAR_CACHE_EVENTS = "KEYS_CLEAR_CACHE_EVENTS";
|
|
||||||
|
|
||||||
public static final String PUBLIC_KEY_STORAGE_INVALIDATION_EVENT = "PUBLIC_KEY_STORAGE_INVALIDATION_EVENT";
|
|
||||||
|
|
||||||
private volatile Cache<String, PublicKeysEntry> keysCache;
|
private volatile Cache<String, PublicKeysEntry> keysCache;
|
||||||
|
|
||||||
private final Map<String, FutureTask<PublicKeysEntry>> tasksInProgress = new ConcurrentHashMap<>();
|
private final Map<String, FutureTask<PublicKeysEntry>> tasksInProgress = new ConcurrentHashMap<>();
|
||||||
|
@ -69,20 +65,6 @@ public class InfinispanPublicKeyStorageProviderFactory implements PublicKeyStora
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (keysCache == null) {
|
if (keysCache == null) {
|
||||||
this.keysCache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME);
|
this.keysCache = session.getProvider(InfinispanConnectionProvider.class).getCache(InfinispanConnectionProvider.KEYS_CACHE_NAME);
|
||||||
|
|
||||||
ClusterProvider cluster = session.getProvider(ClusterProvider.class);
|
|
||||||
cluster.registerListener(PUBLIC_KEY_STORAGE_INVALIDATION_EVENT, (ClusterEvent event) -> {
|
|
||||||
|
|
||||||
PublicKeyStorageInvalidationEvent invalidationEvent = (PublicKeyStorageInvalidationEvent) event;
|
|
||||||
keysCache.remove(invalidationEvent.getCacheKey());
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
cluster.registerListener(KEYS_CLEAR_CACHE_EVENTS, (ClusterEvent event) -> {
|
|
||||||
|
|
||||||
keysCache.clear();
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,6 +129,11 @@ public class InfinispanPublicKeyStorageProviderFactory implements PublicKeyStora
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported() {
|
||||||
|
return !Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
}
|
||||||
|
|
||||||
private static class SessionAndKeyHolder {
|
private static class SessionAndKeyHolder {
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
private final ArrayList<String> cacheKeys;
|
private final ArrayList<String> cacheKeys;
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
org.keycloak.keys.infinispan.InfinispanCachePublicKeyProviderFactory
|
29
model/legacy-private/src/main/java/org/keycloak/models/cache/CachePublicKeyProvider.java
vendored
Normal file
29
model/legacy-private/src/main/java/org/keycloak/models/cache/CachePublicKeyProvider.java
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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;
|
||||||
|
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
|
||||||
|
public interface CachePublicKeyProvider extends Provider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all the cached public keys, so they need to be loaded again
|
||||||
|
*/
|
||||||
|
void clearCache();
|
||||||
|
|
||||||
|
}
|
23
model/legacy-private/src/main/java/org/keycloak/models/cache/CachePublicKeyProviderFactory.java
vendored
Normal file
23
model/legacy-private/src/main/java/org/keycloak/models/cache/CachePublicKeyProviderFactory.java
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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;
|
||||||
|
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
|
||||||
|
public interface CachePublicKeyProviderFactory extends ProviderFactory<CachePublicKeyProvider> {
|
||||||
|
}
|
45
model/legacy-private/src/main/java/org/keycloak/models/cache/CachePublicKeyProviderSpi.java
vendored
Normal file
45
model/legacy-private/src/main/java/org/keycloak/models/cache/CachePublicKeyProviderSpi.java
vendored
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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;
|
||||||
|
|
||||||
|
import org.keycloak.provider.Provider;
|
||||||
|
import org.keycloak.provider.ProviderFactory;
|
||||||
|
import org.keycloak.provider.Spi;
|
||||||
|
|
||||||
|
public class CachePublicKeyProviderSpi implements Spi {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInternal() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "publicKeyCache";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Provider> getProviderClass() {
|
||||||
|
return CachePublicKeyProvider.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends ProviderFactory> getProviderFactoryClass() {
|
||||||
|
return CachePublicKeyProviderFactory.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
org.keycloak.models.cache.CacheUserProviderSpi
|
org.keycloak.models.cache.CacheUserProviderSpi
|
||||||
org.keycloak.models.cache.CacheRealmProviderSpi
|
org.keycloak.models.cache.CacheRealmProviderSpi
|
||||||
|
org.keycloak.models.cache.CachePublicKeyProviderSpi
|
||||||
org.keycloak.storage.client.ClientStorageProviderSpi
|
org.keycloak.storage.client.ClientStorageProviderSpi
|
||||||
org.keycloak.storage.group.GroupStorageProviderSpi
|
org.keycloak.storage.group.GroupStorageProviderSpi
|
||||||
org.keycloak.storage.clientscope.ClientScopeStorageProviderSpi
|
org.keycloak.storage.clientscope.ClientScopeStorageProviderSpi
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.services.resources.admin;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.services.resources.admin.ext.AdminRealmResourceProvider;
|
||||||
|
import org.keycloak.services.resources.admin.ext.AdminRealmResourceProviderFactory;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
|
|
||||||
|
public class ClearKeysCacheRealmAdminProvider implements AdminRealmResourceProviderFactory, AdminRealmResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AdminRealmResourceProvider create(KeycloakSession session) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(Config.Scope config) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "clear-keys-cache";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getResource(KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
|
||||||
|
return new ClearKeysCacheResource(session, realm, auth, adminEvent);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.services.resources.admin;
|
||||||
|
|
||||||
|
import org.keycloak.events.admin.OperationType;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.cache.CachePublicKeyProvider;
|
||||||
|
import org.keycloak.services.resources.admin.permissions.AdminPermissionEvaluator;
|
||||||
|
|
||||||
|
import javax.ws.rs.POST;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
|
|
||||||
|
public class ClearKeysCacheResource {
|
||||||
|
|
||||||
|
protected AdminPermissionEvaluator auth;
|
||||||
|
protected RealmModel realm;
|
||||||
|
private AdminEventBuilder adminEvent;
|
||||||
|
|
||||||
|
@Context
|
||||||
|
protected KeycloakSession session;
|
||||||
|
|
||||||
|
public ClearKeysCacheResource(KeycloakSession session, RealmModel realm, AdminPermissionEvaluator auth, AdminEventBuilder adminEvent) {
|
||||||
|
this.auth = auth;
|
||||||
|
this.realm = realm;
|
||||||
|
this.adminEvent = adminEvent;
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear cache of external public keys (Public keys of clients or Identity providers)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@POST
|
||||||
|
public void clearKeysCache() {
|
||||||
|
auth.realm().requireManageRealm();
|
||||||
|
|
||||||
|
CachePublicKeyProvider cache = session.getProvider(CachePublicKeyProvider.class);
|
||||||
|
if (cache != null) {
|
||||||
|
cache.clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).success();
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,3 +18,4 @@
|
||||||
org.keycloak.services.resources.admin.UserStorageProviderRealmAdminProvider
|
org.keycloak.services.resources.admin.UserStorageProviderRealmAdminProvider
|
||||||
org.keycloak.services.resources.admin.ClearUserCacheRealmAdminProvider
|
org.keycloak.services.resources.admin.ClearUserCacheRealmAdminProvider
|
||||||
org.keycloak.services.resources.admin.ClearRealmCacheRealmAdminProvider
|
org.keycloak.services.resources.admin.ClearRealmCacheRealmAdminProvider
|
||||||
|
org.keycloak.services.resources.admin.ClearKeysCacheRealmAdminProvider
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.map.keys;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.crypto.KeyWrapper;
|
||||||
|
import org.keycloak.keys.PublicKeyLoader;
|
||||||
|
import org.keycloak.keys.PublicKeyStorageProvider;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.FutureTask;
|
||||||
|
|
||||||
|
public class MapPublicKeyStorageProvider implements PublicKeyStorageProvider {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(MapPublicKeyStorageProvider.class);
|
||||||
|
|
||||||
|
private final KeycloakSession session;
|
||||||
|
|
||||||
|
private final Map<String, FutureTask<Map<String, KeyWrapper>>> tasksInProgress;
|
||||||
|
|
||||||
|
public MapPublicKeyStorageProvider(KeycloakSession session, Map<String, FutureTask<Map<String, KeyWrapper>>> tasksInProgress) {
|
||||||
|
this.session = session;
|
||||||
|
this.tasksInProgress = tasksInProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyWrapper getPublicKey(String modelKey, String kid, PublicKeyLoader loader) {
|
||||||
|
return getPublicKey(modelKey, kid, null, loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeyWrapper getFirstPublicKey(String modelKey, String algorithm, PublicKeyLoader loader) {
|
||||||
|
return getPublicKey(modelKey, null, algorithm, loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyWrapper getPublicKey(String modelKey, String kid, String algorithm, PublicKeyLoader loader) {
|
||||||
|
WrapperCallable wrapperCallable = new WrapperCallable(modelKey, loader);
|
||||||
|
FutureTask<Map<String, KeyWrapper>> task = new FutureTask<>(wrapperCallable);
|
||||||
|
FutureTask<Map<String, KeyWrapper>> existing = tasksInProgress.putIfAbsent(modelKey, task);
|
||||||
|
Map<String, KeyWrapper> currentKeys;
|
||||||
|
|
||||||
|
if (existing == null) {
|
||||||
|
task.run();
|
||||||
|
} else {
|
||||||
|
task = existing;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
currentKeys = task.get();
|
||||||
|
|
||||||
|
// Computation finished. Let's see if key is available
|
||||||
|
KeyWrapper publicKey = algorithm != null ? getPublicKeyByAlg(currentKeys, algorithm) : getPublicKey(currentKeys, kid);
|
||||||
|
if (publicKey != null) {
|
||||||
|
return publicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (ExecutionException ee) {
|
||||||
|
throw new RuntimeException("Error when loading public keys: " + ee.getMessage(), ee);
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
throw new RuntimeException("Error. Interrupted when loading public keys", ie);
|
||||||
|
} finally {
|
||||||
|
// Our thread inserted the task. Let's clean
|
||||||
|
if (existing == null) {
|
||||||
|
tasksInProgress.remove(modelKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> availableKids = currentKeys == null ? Collections.emptySet() : currentKeys.keySet();
|
||||||
|
log.warnf("PublicKey wasn't found in the storage. Requested kid: '%s' . Available kids: '%s'", kid, availableKids);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyWrapper getPublicKey(Map<String, KeyWrapper> publicKeys, String kid) {
|
||||||
|
// Backwards compatibility
|
||||||
|
if (kid == null && !publicKeys.isEmpty()) {
|
||||||
|
return publicKeys.values().iterator().next();
|
||||||
|
} else {
|
||||||
|
return publicKeys.get(kid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyWrapper getPublicKeyByAlg(Map<String, KeyWrapper> publicKeys, String algorithm) {
|
||||||
|
if (algorithm == null) return null;
|
||||||
|
for (KeyWrapper keyWrapper : publicKeys.values())
|
||||||
|
if (algorithm.equals(keyWrapper.getAlgorithmOrDefault())) return keyWrapper;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class WrapperCallable implements Callable<Map<String, KeyWrapper>> {
|
||||||
|
|
||||||
|
private final String modelKey;
|
||||||
|
private final PublicKeyLoader delegate;
|
||||||
|
|
||||||
|
public WrapperCallable(String modelKey, PublicKeyLoader delegate) {
|
||||||
|
this.modelKey = modelKey;
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, KeyWrapper> call() throws Exception {
|
||||||
|
Map<String, KeyWrapper> publicKeys = delegate.loadKeys();
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debugf("Public keys retrieved successfully for model %s. New kids: %s", modelKey, publicKeys.keySet().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return publicKeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.map.keys;
|
||||||
|
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
|
import org.keycloak.crypto.KeyWrapper;
|
||||||
|
import org.keycloak.keys.PublicKeyStorageProviderFactory;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.map.common.AbstractEntity;
|
||||||
|
import org.keycloak.models.map.common.AbstractMapProviderFactory;
|
||||||
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.FutureTask;
|
||||||
|
|
||||||
|
public class MapPublicKeyStorageProviderFactory extends AbstractMapProviderFactory<MapPublicKeyStorageProvider, AbstractEntity, Object>
|
||||||
|
implements PublicKeyStorageProviderFactory<MapPublicKeyStorageProvider>, EnvironmentDependentProviderFactory {
|
||||||
|
|
||||||
|
private final Map<String, FutureTask<Map<String, KeyWrapper>>> tasksInProgress = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public MapPublicKeyStorageProviderFactory() {
|
||||||
|
super(Object.class, MapPublicKeyStorageProvider.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapPublicKeyStorageProvider createNew(KeycloakSession session) {
|
||||||
|
return new MapPublicKeyStorageProvider(session, tasksInProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSupported() {
|
||||||
|
return Profile.isFeatureEnabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHelpText() {
|
||||||
|
return "Public key storage provider";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#
|
||||||
|
# Copyright 2022 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory
|
|
@ -47,9 +47,4 @@ public interface PublicKeyStorageProvider extends Provider {
|
||||||
*/
|
*/
|
||||||
KeyWrapper getFirstPublicKey(String modelKey, String algorithm, PublicKeyLoader loader);
|
KeyWrapper getFirstPublicKey(String modelKey, String algorithm, PublicKeyLoader loader);
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all the cached public keys, so they need to be loaded again
|
|
||||||
*/
|
|
||||||
void clearCache();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,5 +22,5 @@ import org.keycloak.provider.ProviderFactory;
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
*/
|
*/
|
||||||
public interface PublicKeyStorageProviderFactory extends ProviderFactory<PublicKeyStorageProvider> {
|
public interface PublicKeyStorageProviderFactory<T extends PublicKeyStorageProvider> extends ProviderFactory<T> {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1079,23 +1079,6 @@ public class RealmAdminResource {
|
||||||
return stripForExport(session, rep);
|
return stripForExport(session, rep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear cache of external public keys (Public keys of clients or Identity providers)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@Path("clear-keys-cache")
|
|
||||||
@POST
|
|
||||||
public void clearKeysCache() {
|
|
||||||
auth.realm().requireManageRealm();
|
|
||||||
|
|
||||||
PublicKeyStorageProvider cache = session.getProvider(PublicKeyStorageProvider.class);
|
|
||||||
if (cache != null) {
|
|
||||||
cache.clearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
adminEvent.operation(OperationType.ACTION).resourcePath(session.getContext().getUri()).success();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Path("keys")
|
@Path("keys")
|
||||||
public KeyResource keys() {
|
public KeyResource keys() {
|
||||||
KeyResource resource = new KeyResource(realm, session, this.auth);
|
KeyResource resource = new KeyResource(realm, session, this.auth);
|
||||||
|
|
|
@ -849,9 +849,11 @@
|
||||||
<keycloak.eventsStore.provider>map</keycloak.eventsStore.provider>
|
<keycloak.eventsStore.provider>map</keycloak.eventsStore.provider>
|
||||||
<keycloak.actionToken.provider>map</keycloak.actionToken.provider>
|
<keycloak.actionToken.provider>map</keycloak.actionToken.provider>
|
||||||
<keycloak.singleUseObject.provider>map</keycloak.singleUseObject.provider>
|
<keycloak.singleUseObject.provider>map</keycloak.singleUseObject.provider>
|
||||||
|
<keycloak.publicKeyStorage.provider>map</keycloak.publicKeyStorage.provider>
|
||||||
<keycloak.authorizationCache.enabled>false</keycloak.authorizationCache.enabled>
|
<keycloak.authorizationCache.enabled>false</keycloak.authorizationCache.enabled>
|
||||||
<keycloak.realmCache.enabled>false</keycloak.realmCache.enabled>
|
<keycloak.realmCache.enabled>false</keycloak.realmCache.enabled>
|
||||||
<keycloak.userCache.enabled>false</keycloak.userCache.enabled>
|
<keycloak.userCache.enabled>false</keycloak.userCache.enabled>
|
||||||
|
<keycloak.publicKeyCache.enabled>false</keycloak.publicKeyCache.enabled>
|
||||||
<keycloak.userSessionPersister.provider></keycloak.userSessionPersister.provider>
|
<keycloak.userSessionPersister.provider></keycloak.userSessionPersister.provider>
|
||||||
</systemPropertyVariables>
|
</systemPropertyVariables>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.keycloak.admin.client.resource.RealmResource;
|
import org.keycloak.admin.client.resource.RealmResource;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.util.*;
|
import org.keycloak.common.util.*;
|
||||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||||
import org.keycloak.crypto.Algorithm;
|
import org.keycloak.crypto.Algorithm;
|
||||||
|
@ -39,6 +40,7 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
|
||||||
import org.keycloak.representations.idm.KeysMetadataRepresentation;
|
import org.keycloak.representations.idm.KeysMetadataRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.ProfileAssume;
|
||||||
import org.keycloak.testsuite.admin.ApiUtil;
|
import org.keycloak.testsuite.admin.ApiUtil;
|
||||||
import org.keycloak.testsuite.client.resources.TestingCacheResource;
|
import org.keycloak.testsuite.client.resources.TestingCacheResource;
|
||||||
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
|
||||||
|
@ -95,6 +97,8 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignatureVerificationJwksUrl() throws Exception {
|
public void testSignatureVerificationJwksUrl() throws Exception {
|
||||||
|
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
|
||||||
// Configure OIDC identity provider with JWKS URL
|
// Configure OIDC identity provider with JWKS URL
|
||||||
updateIdentityProviderWithJwksUrl();
|
updateIdentityProviderWithJwksUrl();
|
||||||
|
|
||||||
|
@ -303,6 +307,8 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClearKeysCache() throws Exception {
|
public void testClearKeysCache() throws Exception {
|
||||||
|
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
|
||||||
// Configure OIDC identity provider with JWKS URL
|
// Configure OIDC identity provider with JWKS URL
|
||||||
updateIdentityProviderWithJwksUrl();
|
updateIdentityProviderWithJwksUrl();
|
||||||
|
|
||||||
|
@ -328,6 +334,8 @@ public class KcOIDCBrokerWithSignatureTest extends AbstractBaseBrokerTest {
|
||||||
// Test that when I update identityProvier, then the record in publicKey cache is cleared and it's not possible to authenticate with it anymore
|
// Test that when I update identityProvier, then the record in publicKey cache is cleared and it's not possible to authenticate with it anymore
|
||||||
@Test
|
@Test
|
||||||
public void testPublicKeyCacheInvalidatedWhenProviderUpdated() throws Exception {
|
public void testPublicKeyCacheInvalidatedWhenProviderUpdated() throws Exception {
|
||||||
|
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
|
||||||
// Configure OIDC identity provider with JWKS URL
|
// Configure OIDC identity provider with JWKS URL
|
||||||
updateIdentityProviderWithJwksUrl();
|
updateIdentityProviderWithJwksUrl();
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.junit.Test;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
|
import org.keycloak.adapters.authentication.JWTClientCredentialsProvider;
|
||||||
import org.keycloak.client.registration.Auth;
|
import org.keycloak.client.registration.Auth;
|
||||||
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.common.util.KeycloakUriBuilder;
|
import org.keycloak.common.util.KeycloakUriBuilder;
|
||||||
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
|
||||||
import org.keycloak.constants.ServiceUrlConstants;
|
import org.keycloak.constants.ServiceUrlConstants;
|
||||||
|
@ -58,6 +59,7 @@ import org.keycloak.representations.idm.ClientInitialAccessPresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
import org.keycloak.representations.oidc.OIDCClientRepresentation;
|
||||||
import org.keycloak.testsuite.Assert;
|
import org.keycloak.testsuite.Assert;
|
||||||
|
import org.keycloak.testsuite.ProfileAssume;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||||
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
|
import org.keycloak.testsuite.client.resources.TestApplicationResourceUrls;
|
||||||
|
@ -179,6 +181,8 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTwoClientsWithSameKid() throws Exception {
|
public void testTwoClientsWithSameKid() throws Exception {
|
||||||
|
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
|
||||||
// Create client with manually set "kid"
|
// Create client with manually set "kid"
|
||||||
OIDCClientRepresentation response = createClientWithManuallySetKid("a1");
|
OIDCClientRepresentation response = createClientWithManuallySetKid("a1");
|
||||||
|
|
||||||
|
@ -218,6 +222,8 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPublicKeyCacheInvalidatedWhenUpdatingClient() throws Exception {
|
public void testPublicKeyCacheInvalidatedWhenUpdatingClient() throws Exception {
|
||||||
|
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
|
||||||
OIDCClientRepresentation response = createClientWithManuallySetKid("a1");
|
OIDCClientRepresentation response = createClientWithManuallySetKid("a1");
|
||||||
|
|
||||||
Map<String, String> generatedKeys = testingClient.testApp().oidcClientEndpoints().getKeysAsPem();
|
Map<String, String> generatedKeys = testingClient.testApp().oidcClientEndpoints().getKeysAsPem();
|
||||||
|
@ -271,6 +277,8 @@ public class OIDCJwksClientRegistrationTest extends AbstractClientRegistrationTe
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void createClientWithJWKSURI_rotateClientKeys() throws Exception {
|
public void createClientWithJWKSURI_rotateClientKeys() throws Exception {
|
||||||
|
ProfileAssume.assumeFeatureDisabled(Profile.Feature.MAP_STORAGE);
|
||||||
|
|
||||||
OIDCClientRepresentation clientRep = createRep();
|
OIDCClientRepresentation clientRep = createRep();
|
||||||
|
|
||||||
clientRep.setGrantTypes(Collections.singletonList(OAuth2Constants.CLIENT_CREDENTIALS));
|
clientRep.setGrantTypes(Collections.singletonList(OAuth2Constants.CLIENT_CREDENTIALS));
|
||||||
|
|
|
@ -160,6 +160,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"publicKeyStorage": {
|
||||||
|
"provider": "${keycloak.publicKeyStorage.provider:infinispan}",
|
||||||
|
"map": {
|
||||||
|
"storage": {
|
||||||
|
"provider": "${keycloak.publicKeyStorage.map.storage.provider:concurrenthashmap}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"mapStorage": {
|
"mapStorage": {
|
||||||
"concurrenthashmap": {
|
"concurrenthashmap": {
|
||||||
"dir": "${project.build.directory:target}",
|
"dir": "${project.build.directory:target}",
|
||||||
|
@ -209,6 +218,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"publicKeyCache": {
|
||||||
|
"default" : {
|
||||||
|
"enabled": "${keycloak.publicKeyCache.enabled:true}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"timer": {
|
"timer": {
|
||||||
"provider": "basic"
|
"provider": "basic"
|
||||||
},
|
},
|
||||||
|
|
|
@ -32,6 +32,7 @@ import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
||||||
import org.keycloak.models.map.client.MapClientProviderFactory;
|
import org.keycloak.models.map.client.MapClientProviderFactory;
|
||||||
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
|
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
|
||||||
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
|
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
|
||||||
|
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
|
||||||
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
|
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
|
||||||
import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory;
|
import org.keycloak.models.map.storage.hotRod.connections.DefaultHotRodConnectionProviderFactory;
|
||||||
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProviderFactory;
|
import org.keycloak.models.map.storage.hotRod.connections.HotRodConnectionProviderFactory;
|
||||||
|
@ -78,6 +79,7 @@ public class HotRodMapStorage extends KeycloakModelParameters {
|
||||||
cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
cf.spi(AuthenticationSessionSpi.PROVIDER_ID).provider(MapRootAuthenticationSessionProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(ActionTokenStoreSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
.spi(ActionTokenStoreSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
||||||
|
.spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi("client").provider(MapClientProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
.spi("client").provider(MapClientProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
.spi("clientScope").provider(MapClientScopeProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi("group").provider(MapGroupProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
.spi("group").provider(MapGroupProviderFactory.PROVIDER_ID).config(STORAGE_CONFIG, HotRodMapStorageProviderFactory.PROVIDER_ID)
|
||||||
|
|
|
@ -19,8 +19,12 @@ package org.keycloak.testsuite.model.parameters;
|
||||||
import org.keycloak.cluster.infinispan.InfinispanClusterProviderFactory;
|
import org.keycloak.cluster.infinispan.InfinispanClusterProviderFactory;
|
||||||
import org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory;
|
import org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory;
|
||||||
import org.keycloak.connections.infinispan.InfinispanConnectionSpi;
|
import org.keycloak.connections.infinispan.InfinispanConnectionSpi;
|
||||||
|
import org.keycloak.keys.PublicKeyStorageSpi;
|
||||||
|
import org.keycloak.keys.infinispan.InfinispanCachePublicKeyProviderFactory;
|
||||||
|
import org.keycloak.keys.infinispan.InfinispanPublicKeyStorageProviderFactory;
|
||||||
import org.keycloak.models.ActionTokenStoreSpi;
|
import org.keycloak.models.ActionTokenStoreSpi;
|
||||||
import org.keycloak.models.SingleUseObjectSpi;
|
import org.keycloak.models.SingleUseObjectSpi;
|
||||||
|
import org.keycloak.models.cache.CachePublicKeyProviderSpi;
|
||||||
import org.keycloak.models.session.UserSessionPersisterSpi;
|
import org.keycloak.models.session.UserSessionPersisterSpi;
|
||||||
import org.keycloak.models.sessions.infinispan.InfinispanActionTokenStoreProviderFactory;
|
import org.keycloak.models.sessions.infinispan.InfinispanActionTokenStoreProviderFactory;
|
||||||
import org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory;
|
import org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionProviderFactory;
|
||||||
|
@ -63,6 +67,8 @@ public class Infinispan extends KeycloakModelParameters {
|
||||||
.add(UserSessionPersisterSpi.class)
|
.add(UserSessionPersisterSpi.class)
|
||||||
.add(ActionTokenStoreSpi.class)
|
.add(ActionTokenStoreSpi.class)
|
||||||
.add(SingleUseObjectSpi.class)
|
.add(SingleUseObjectSpi.class)
|
||||||
|
.add(PublicKeyStorageSpi.class)
|
||||||
|
.add(CachePublicKeyProviderSpi.class)
|
||||||
|
|
||||||
.add(LegacySessionSupportSpi.class) // necessary as it will call session.userCredentialManager().onCache()
|
.add(LegacySessionSupportSpi.class) // necessary as it will call session.userCredentialManager().onCache()
|
||||||
|
|
||||||
|
@ -80,6 +86,8 @@ public class Infinispan extends KeycloakModelParameters {
|
||||||
.add(InfinispanSingleUseObjectProviderFactory.class)
|
.add(InfinispanSingleUseObjectProviderFactory.class)
|
||||||
.add(StickySessionEncoderProviderFactory.class)
|
.add(StickySessionEncoderProviderFactory.class)
|
||||||
.add(TimerProviderFactory.class)
|
.add(TimerProviderFactory.class)
|
||||||
|
.add(InfinispanPublicKeyStorageProviderFactory.class)
|
||||||
|
.add(InfinispanCachePublicKeyProviderFactory.class)
|
||||||
.add(LegacySessionSupportProviderFactory.class)
|
.add(LegacySessionSupportProviderFactory.class)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.keycloak.models.map.client.MapClientProviderFactory;
|
||||||
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
|
import org.keycloak.models.map.clientscope.MapClientScopeProviderFactory;
|
||||||
import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
|
import org.keycloak.models.map.deploymentState.MapDeploymentStateProviderFactory;
|
||||||
import org.keycloak.models.map.group.MapGroupProviderFactory;
|
import org.keycloak.models.map.group.MapGroupProviderFactory;
|
||||||
|
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
|
||||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
|
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
|
||||||
import org.keycloak.models.map.realm.MapRealmProviderFactory;
|
import org.keycloak.models.map.realm.MapRealmProviderFactory;
|
||||||
import org.keycloak.models.map.role.MapRoleProviderFactory;
|
import org.keycloak.models.map.role.MapRoleProviderFactory;
|
||||||
|
@ -99,6 +100,7 @@ public class JpaMapStorage extends KeycloakModelParameters {
|
||||||
.spi("dblock").provider(NoLockingDBLockProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
.spi("dblock").provider(NoLockingDBLockProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(ActionTokenStoreSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
.spi(ActionTokenStoreSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
|
.spi(SingleUseObjectSpi.NAME).provider(MapSingleUseObjectProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, JpaMapStorageProviderFactory.PROVIDER_ID)
|
||||||
|
.spi("publicKeyStorage").provider(MapPublicKeyStorageProviderFactory.PROVIDER_ID) .config(STORAGE_CONFIG, ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config("storage-user-sessions.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
.spi(UserSessionSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config("storage-user-sessions.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.config("storage-client-sessions.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
.config("storage-client-sessions.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(EventStoreSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config("storage-admin-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID)
|
.spi(EventStoreSpi.NAME).provider(MapUserSessionProviderFactory.PROVIDER_ID) .config("storage-admin-events.provider", JpaMapStorageProviderFactory.PROVIDER_ID)
|
||||||
|
|
|
@ -105,7 +105,8 @@ public class LdapMapStorage extends KeycloakModelParameters {
|
||||||
.spi(EventStoreSpi.NAME).config("map.storage-admin-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
.spi(EventStoreSpi.NAME).config("map.storage-admin-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(EventStoreSpi.NAME).config("map.storage-auth-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
.spi(EventStoreSpi.NAME).config("map.storage-auth-events.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(ActionTokenStoreSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
.spi(ActionTokenStoreSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
.spi(SingleUseObjectSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID);
|
.spi(SingleUseObjectSpi.NAME).config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID)
|
||||||
|
.spi("publicKeyStorage").config("map.storage.provider", ConcurrentHashMapStorageProviderFactory.PROVIDER_ID);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.keycloak.testsuite.model.parameters;
|
||||||
|
|
||||||
import org.keycloak.authorization.store.StoreFactorySpi;
|
import org.keycloak.authorization.store.StoreFactorySpi;
|
||||||
import org.keycloak.events.EventStoreSpi;
|
import org.keycloak.events.EventStoreSpi;
|
||||||
|
import org.keycloak.keys.PublicKeyStorageSpi;
|
||||||
import org.keycloak.models.ActionTokenStoreProviderFactory;
|
import org.keycloak.models.ActionTokenStoreProviderFactory;
|
||||||
import org.keycloak.models.ActionTokenStoreSpi;
|
import org.keycloak.models.ActionTokenStoreSpi;
|
||||||
import org.keycloak.models.DeploymentStateSpi;
|
import org.keycloak.models.DeploymentStateSpi;
|
||||||
|
@ -29,6 +30,7 @@ import org.keycloak.models.dblock.NoLockingDBLockProviderFactory;
|
||||||
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
import org.keycloak.models.map.authSession.MapRootAuthenticationSessionProviderFactory;
|
||||||
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
import org.keycloak.models.map.authorization.MapAuthorizationStoreFactory;
|
||||||
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
|
import org.keycloak.models.map.events.MapEventStoreProviderFactory;
|
||||||
|
import org.keycloak.models.map.keys.MapPublicKeyStorageProviderFactory;
|
||||||
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
|
import org.keycloak.models.map.loginFailure.MapUserLoginFailureProviderFactory;
|
||||||
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
|
import org.keycloak.models.map.singleUseObject.MapSingleUseObjectProviderFactory;
|
||||||
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
|
import org.keycloak.models.map.storage.chm.ConcurrentHashMapStorageProviderFactory;
|
||||||
|
@ -59,6 +61,7 @@ public class Map extends KeycloakModelParameters {
|
||||||
.add(AuthenticationSessionSpi.class)
|
.add(AuthenticationSessionSpi.class)
|
||||||
.add(ActionTokenStoreSpi.class)
|
.add(ActionTokenStoreSpi.class)
|
||||||
.add(SingleUseObjectSpi.class)
|
.add(SingleUseObjectSpi.class)
|
||||||
|
.add(PublicKeyStorageSpi.class)
|
||||||
.add(MapStorageSpi.class)
|
.add(MapStorageSpi.class)
|
||||||
|
|
||||||
.build();
|
.build();
|
||||||
|
@ -79,6 +82,7 @@ public class Map extends KeycloakModelParameters {
|
||||||
.add(MapEventStoreProviderFactory.class)
|
.add(MapEventStoreProviderFactory.class)
|
||||||
.add(ActionTokenStoreProviderFactory.class)
|
.add(ActionTokenStoreProviderFactory.class)
|
||||||
.add(SingleUseObjectProviderFactory.class)
|
.add(SingleUseObjectProviderFactory.class)
|
||||||
|
.add(MapPublicKeyStorageProviderFactory.class)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public Map() {
|
public Map() {
|
||||||
|
@ -102,6 +106,7 @@ public class Map extends KeycloakModelParameters {
|
||||||
.spi(UserLoginFailureSpi.NAME).defaultProvider(MapUserLoginFailureProviderFactory.PROVIDER_ID)
|
.spi(UserLoginFailureSpi.NAME).defaultProvider(MapUserLoginFailureProviderFactory.PROVIDER_ID)
|
||||||
.spi("dblock").defaultProvider(NoLockingDBLockProviderFactory.PROVIDER_ID)
|
.spi("dblock").defaultProvider(NoLockingDBLockProviderFactory.PROVIDER_ID)
|
||||||
.spi(EventStoreSpi.NAME).defaultProvider(MapEventStoreProviderFactory.PROVIDER_ID)
|
.spi(EventStoreSpi.NAME).defaultProvider(MapEventStoreProviderFactory.PROVIDER_ID)
|
||||||
|
.spi("publicKeyStorage").defaultProvider(MapPublicKeyStorageProviderFactory.PROVIDER_ID)
|
||||||
;
|
;
|
||||||
cf.spi(MapStorageSpi.NAME).provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID).config("keyType.single-use-objects", "string");
|
cf.spi(MapStorageSpi.NAME).provider(ConcurrentHashMapStorageProviderFactory.PROVIDER_ID).config("keyType.single-use-objects", "string");
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,9 +321,11 @@
|
||||||
<systemProperty><key>keycloak.actionToken.provider</key><value>map</value></systemProperty>
|
<systemProperty><key>keycloak.actionToken.provider</key><value>map</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.singleUseObject.provider</key><value>map</value></systemProperty>
|
<systemProperty><key>keycloak.singleUseObject.provider</key><value>map</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.eventsStore.provider</key><value>map</value></systemProperty>
|
<systemProperty><key>keycloak.eventsStore.provider</key><value>map</value></systemProperty>
|
||||||
|
<systemProperty><key>keycloak.publicKeyStorage.provider</key><value>map</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.authorizationCache.enabled</key><value>false</value></systemProperty>
|
<systemProperty><key>keycloak.authorizationCache.enabled</key><value>false</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.realmCache.enabled</key><value>false</value></systemProperty>
|
<systemProperty><key>keycloak.realmCache.enabled</key><value>false</value></systemProperty>
|
||||||
<systemProperty><key>keycloak.userCache.enabled</key><value>false</value></systemProperty>
|
<systemProperty><key>keycloak.userCache.enabled</key><value>false</value></systemProperty>
|
||||||
|
<systemProperty><key>keycloak.publicKeyCache.enabled</key><value>false</value></systemProperty>
|
||||||
</systemProperties>
|
</systemProperties>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
|
@ -132,6 +132,15 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"publicKeyStorage": {
|
||||||
|
"provider": "${keycloak.publicKeyStorage.provider:infinispan}",
|
||||||
|
"map": {
|
||||||
|
"storage": {
|
||||||
|
"provider": "${keycloak.publicKeyStorage.map.storage.provider:concurrenthashmap}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"mapStorage": {
|
"mapStorage": {
|
||||||
"provider": "${keycloak.mapStorage.provider:}",
|
"provider": "${keycloak.mapStorage.provider:}",
|
||||||
"concurrenthashmap": {
|
"concurrenthashmap": {
|
||||||
|
@ -234,6 +243,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"publicKeyCache": {
|
||||||
|
"default" : {
|
||||||
|
"enabled": "${keycloak.publicKeyCache.enabled:true}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
"authorizationCache": {
|
"authorizationCache": {
|
||||||
"default": {
|
"default": {
|
||||||
"enabled": "${keycloak.authorizationCache.enabled:true}"
|
"enabled": "${keycloak.authorizationCache.enabled:true}"
|
||||||
|
|
Loading…
Reference in a new issue