Added LegacySessionSupport SPI

While some methods around onCache() are still called from the legacy code, all other methods log a warning with a stacktrace.
This commit is contained in:
Alexander Schwartz 2022-05-06 15:28:14 +02:00 committed by Hynek Mlnařík
parent 6f287e7ded
commit 14a369a8cc
12 changed files with 200 additions and 29 deletions

View file

@ -23,6 +23,7 @@ import org.keycloak.credential.CredentialInput;
import org.keycloak.models.ClientScopeModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.LegacySessionSupportProvider;
import org.keycloak.models.cache.infinispan.events.InvalidationEvent;
import org.keycloak.common.constants.ServiceAccountConstants;
import org.keycloak.component.ComponentModel;
@ -372,7 +373,7 @@ public class UserCacheSession implements UserCache.Streams, OnCreateComponent, O
private void onCache(RealmModel realm, UserAdapter adapter, UserModel delegate) {
((OnUserCache)getDelegate()).onCache(realm, adapter, delegate);
((OnUserCache)session.userCredentialManager()).onCache(realm, adapter, delegate);
((OnUserCache) session.getProvider(LegacySessionSupportProvider.class).userCredentialManager()).onCache(realm, adapter, delegate);
}
@Override

View file

@ -16,6 +16,7 @@
*/
package org.keycloak.credential;
import org.jboss.logging.Logger;
import org.keycloak.common.util.reflections.Types;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.KeycloakSession;
@ -33,8 +34,10 @@ import java.util.stream.Stream;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class UserCredentialStoreManager
implements UserCredentialManager.Streams, OnUserCache {
@Deprecated
public class UserCredentialStoreManager implements UserCredentialManager.Streams, OnUserCache {
private final static Logger log = Logger.getLogger(UserCredentialStoreManager.class);
private final KeycloakSession session;
@ -45,90 +48,91 @@ public class UserCredentialStoreManager
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public void updateCredential(RealmModel realm, UserModel user, CredentialModel cred) {
// TODO: no longer used in non-legacy code, can be removed
warnAboutUsage();
user.getUserCredentialManager().updateStoredCredential(cred);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public CredentialModel createCredential(RealmModel realm, UserModel user, CredentialModel cred) {
// TODO: no longer used in non-legacy code, can be removed
warnAboutUsage();
return user.getUserCredentialManager().createStoredCredential(cred);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public boolean removeStoredCredential(RealmModel realm, UserModel user, String id) {
// TODO: no longer used in non-legacy code, can be removed
warnAboutUsage();
return user.getUserCredentialManager().removeStoredCredentialById(id);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public CredentialModel getStoredCredentialById(RealmModel realm, UserModel user, String id) {
// TODO: no longer used in non-legacy code, can be removed
warnAboutUsage();
return user.getUserCredentialManager().getStoredCredentialById(id);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public Stream<CredentialModel> getStoredCredentialsStream(RealmModel realm, UserModel user) {
// TODO: no longer used in non-legacy code, can be removed
warnAboutUsage();
return user.getUserCredentialManager().getStoredCredentialsStream();
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public Stream<CredentialModel> getStoredCredentialsByTypeStream(RealmModel realm, UserModel user, String type) {
// TODO: no longer used in non-legacy code, can be removed
warnAboutUsage();
return user.getUserCredentialManager().getStoredCredentialsByTypeStream(type);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
// TODO: no longer used in non-legacy code, can be removed
warnAboutUsage();
return user.getUserCredentialManager().getStoredCredentialByNameAndType(name, type);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public boolean moveCredentialTo(RealmModel realm, UserModel user, String id, String newPreviousCredentialId){
// TODO: no longer used, can be removed
warnAboutUsage();
return user.getUserCredentialManager().moveStoredCredentialTo(id, newPreviousCredentialId);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public boolean isValid(RealmModel realm, UserModel user, CredentialInput... inputs) {
// TODO: no longer used, can be removed
warnAboutUsage();
return isValid(realm, user, Arrays.asList(inputs));
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public CredentialModel createCredentialThroughProvider(RealmModel realm, UserModel user, CredentialModel model){
// TODO: no longer used, can be removed
warnAboutUsage();
return user.getUserCredentialManager().createCredentialThroughProvider(model);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public void updateCredentialLabel(RealmModel realm, UserModel user, String credentialId, String userLabel){
// TODO: no longer used, can be removed
warnAboutUsage();
user.getUserCredentialManager().updateCredentialLabel(credentialId, userLabel);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public boolean isValid(RealmModel realm, UserModel user, List<CredentialInput> inputs) {
// TODO: no longer used, can be removed
warnAboutUsage();
return user.getUserCredentialManager().isValid(inputs);
}
@Deprecated // Keep this up to and including Keycloak 19, then inline
public static <T> Stream<T> getCredentialProviders(KeycloakSession session, Class<T> type) {
// TODO: no longer used, can be removed
// called via #onCache()
// warnAboutUsage();
return session.getKeycloakSessionFactory().getProviderFactoriesStream(CredentialProvider.class)
.filter(f -> Types.supports(type, f, CredentialProviderFactory.class))
.map(f -> (T) session.getProvider(CredentialProvider.class, f.getId()));
@ -137,56 +141,57 @@ public class UserCredentialStoreManager
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) {
// TODO: no longer used, can be removed
warnAboutUsage();
return user.getUserCredentialManager().updateCredential(input);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
// TODO: no longer used, can be removed
warnAboutUsage();
user.getUserCredentialManager().disableCredentialType(credentialType);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public Stream<String> getDisableableCredentialTypesStream(RealmModel realm, UserModel user) {
// TODO: no longer used, can be removed
warnAboutUsage();
return user.getUserCredentialManager().getDisableableCredentialTypesStream();
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public boolean isConfiguredFor(RealmModel realm, UserModel user, String type) {
// TODO: no longer used, can be removed
warnAboutUsage();
return user.getUserCredentialManager().isConfiguredFor(type);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public boolean isConfiguredLocally(RealmModel realm, UserModel user, String type) {
// TODO: no longer used, can be removed
warnAboutUsage();
return user.getUserCredentialManager().isConfiguredLocally(type);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public CredentialValidationOutput authenticate(KeycloakSession session, RealmModel realm, CredentialInput input) {
// TODO: no longer used, can be removed
warnAboutUsage();
return session.users().getUserByCredential(realm, input);
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, then remove it together with the OnUserCache class
public void onCache(RealmModel realm, CachedUserModel user, UserModel delegate) {
// TODO: only called from legacy code (infinispan), can be removed
// called by UserCacheSession#onCache, therefore don't warn here
// warnAboutUsage();
getCredentialProviders(session, OnUserCache.class).forEach(validator -> validator.onCache(realm, user, delegate));
}
@Override
@Deprecated // Keep this up to and including Keycloak 19, the use methods on user.getUserCredentialManager() instead
public Stream<String> getConfiguredUserStorageCredentialTypesStream(RealmModel realm, UserModel user) {
// TODO: no longer used, can be removed
warnAboutUsage();
return user.getUserCredentialManager().getConfiguredUserStorageCredentialTypesStream();
}
@ -195,4 +200,11 @@ public class UserCredentialStoreManager
}
private static void warnAboutUsage() {
if (log.isEnabled(Logger.Level.WARN)) {
// check if warning is enabled first before constructing the exception that is expensive to construct
log.warn("Calls to session.userCredentialManager() now deprecated. Use user.getUserCredentialManager() instead!", new RuntimeException());
}
}
}

View file

@ -0,0 +1,37 @@
package org.keycloak.services.legacysessionsupport;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.LegacySessionSupportProvider;
/**
* @author Alexander Schwartz
*/
public class LegacySessionSupportProviderFactoryImpl implements LegacySessionSupportProviderFactory<LegacySessionSupportProvider> {
private static final String PROVIDER_ID = "default";
@Override
public LegacySessionSupportProvider create(KeycloakSession session) {
return new LegacySessionSupportProviderImpl(session);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -0,0 +1,31 @@
package org.keycloak.services.legacysessionsupport;
import org.keycloak.credential.UserCredentialStoreManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserCredentialManager;
import org.keycloak.models.LegacySessionSupportProvider;
/**
* @author Alexander Schwartz
*/
public class LegacySessionSupportProviderImpl implements LegacySessionSupportProvider {
private final KeycloakSession session;
public LegacySessionSupportProviderImpl(KeycloakSession session) {
this.session = session;
}
@Override
public void close() {
}
@Override
@Deprecated
public UserCredentialManager userCredentialManager() {
// UserCacheSession calls session.userCredentialManager().onCache(), therefore can't trigger a warning here at the moment.
return new UserCredentialStoreManager(session);
}
}

View file

@ -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.services.legacysessionsupport.LegacySessionSupportProviderFactoryImpl

View file

@ -0,0 +1,15 @@
package org.keycloak.models;
import org.keycloak.provider.Provider;
/**
* Support for elements in Keycloak's session that are deprecated.
* This allows the deprecated implementations to be moved to the legacy module.
*
* @author Alexander Schwartz
*/
public interface LegacySessionSupportProvider extends Provider {
@Deprecated
UserCredentialManager userCredentialManager();
}

View file

@ -68,7 +68,9 @@ public interface SingleUserCredentialManager {
@Deprecated
boolean isConfiguredLocally(String type);
// TODO: not needed for new store? -> no, will be removed without replacement
Stream<String> getConfiguredUserStorageCredentialTypesStream();
// TODO: not needed for new store? -> no, will be removed without replacement
CredentialModel createCredentialThroughProvider(CredentialModel model);
}

View file

@ -18,7 +18,6 @@ package org.keycloak.services;
import org.keycloak.component.ComponentFactory;
import org.keycloak.component.ComponentModel;
import org.keycloak.credential.UserCredentialStoreManager;
import org.keycloak.jose.jws.DefaultTokenManager;
import org.keycloak.keys.DefaultKeyManager;
import org.keycloak.models.ClientProvider;
@ -45,6 +44,7 @@ import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.InvalidationHandler.InvalidableObjectType;
import org.keycloak.provider.InvalidationHandler.ObjectType;
import org.keycloak.services.clientpolicy.ClientPolicyManager;
import org.keycloak.models.LegacySessionSupportProvider;
import org.keycloak.sessions.AuthenticationSessionProvider;
import org.keycloak.storage.DatastoreProvider;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
@ -76,12 +76,13 @@ public class DefaultKeycloakSession implements KeycloakSession {
private final Map<String, Object> attributes = new HashMap<>();
private final Map<InvalidableObjectType, Set<Object>> invalidationMap = new HashMap<>();
private DatastoreProvider datastoreProvider;
private UserCredentialStoreManager userCredentialStorageManager;
@Deprecated
private UserCredentialManager userCredentialStorageManager;
private UserSessionProvider sessionProvider;
private UserLoginFailureProvider userLoginFailureProvider;
private AuthenticationSessionProvider authenticationSessionProvider;
private UserFederatedStorageProvider userFederatedStorageProvider;
private KeycloakContext context;
private final KeycloakContext context;
private KeyManager keyManager;
private ThemeManager themeManager;
private TokenManager tokenManager;
@ -229,8 +230,15 @@ public class DefaultKeycloakSession implements KeycloakSession {
}
@Override
@Deprecated
public UserCredentialManager userCredentialManager() {
if (userCredentialStorageManager == null) userCredentialStorageManager = new UserCredentialStoreManager(this);
if (userCredentialStorageManager == null) {
LegacySessionSupportProvider provider = this.getProvider(LegacySessionSupportProvider.class);
if (provider == null) {
throw new IllegalStateException("legacy support for a UserCredentialManager is not enabled");
}
userCredentialStorageManager = provider.userCredentialManager();
}
return userCredentialStorageManager;
}

View file

@ -0,0 +1,10 @@
package org.keycloak.services.legacysessionsupport;
import org.keycloak.models.LegacySessionSupportProvider;
import org.keycloak.provider.ProviderFactory;
/**
* @author Alexander Schwartz
*/
public interface LegacySessionSupportProviderFactory<T extends LegacySessionSupportProvider> extends ProviderFactory<T> {
}

View file

@ -0,0 +1,32 @@
package org.keycloak.services.legacysessionsupport;
import org.keycloak.models.LegacySessionSupportProvider;
import org.keycloak.provider.Provider;
import org.keycloak.provider.Spi;
/**
* @author Alexander Schwartz
*/
public class LegacySessionSupportSpi implements Spi {
@Override
public boolean isInternal() {
return true;
}
@Override
public String getName() {
return "legacy-session-support";
}
@Override
public Class<? extends Provider> getProviderClass() {
return LegacySessionSupportProvider.class;
}
@Override
public Class<? extends LegacySessionSupportProviderFactory> getProviderFactoryClass() {
return LegacySessionSupportProviderFactory.class;
}
}

View file

@ -28,4 +28,4 @@ org.keycloak.protocol.oidc.grants.ciba.channel.AuthenticationChannelSpi
org.keycloak.protocol.oidc.grants.ciba.resolvers.CIBALoginUserResolverSpi
org.keycloak.protocol.oidc.rar.AuthorizationRequestParserSpi
org.keycloak.services.resources.admin.ext.AdminRealmResourceSpi
org.keycloak.credential.SingleUserCredentialManagerSpi
org.keycloak.services.legacysessionsupport.LegacySessionSupportSpi

View file

@ -27,6 +27,8 @@ import org.keycloak.models.sessions.infinispan.InfinispanAuthenticationSessionPr
import org.keycloak.models.sessions.infinispan.InfinispanSingleUseObjectProviderFactory;
import org.keycloak.models.sessions.infinispan.InfinispanUserLoginFailureProviderFactory;
import org.keycloak.models.sessions.infinispan.InfinispanUserSessionProviderFactory;
import org.keycloak.services.legacysessionsupport.LegacySessionSupportProviderFactory;
import org.keycloak.services.legacysessionsupport.LegacySessionSupportSpi;
import org.keycloak.sessions.AuthenticationSessionSpi;
import org.keycloak.sessions.StickySessionEncoderProviderFactory;
import org.keycloak.sessions.StickySessionEncoderSpi;
@ -62,6 +64,8 @@ public class Infinispan extends KeycloakModelParameters {
.add(ActionTokenStoreSpi.class)
.add(SingleUseObjectSpi.class)
.add(LegacySessionSupportSpi.class) // necessary as it will call session.userCredentialManager().onCache()
.build();
static final Set<Class<? extends ProviderFactory>> ALLOWED_FACTORIES = ImmutableSet.<Class<? extends ProviderFactory>>builder()
@ -76,6 +80,7 @@ public class Infinispan extends KeycloakModelParameters {
.add(InfinispanSingleUseObjectProviderFactory.class)
.add(StickySessionEncoderProviderFactory.class)
.add(TimerProviderFactory.class)
.add(LegacySessionSupportProviderFactory.class)
.build();
@Override