diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java index e100c298f7..6bed4cf3aa 100755 --- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java +++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java @@ -48,6 +48,7 @@ import org.keycloak.models.cache.infinispan.events.UserFederationLinkRemovedEven import org.keycloak.models.cache.infinispan.events.UserFederationLinkUpdatedEvent; import org.keycloak.models.cache.infinispan.events.UserFullInvalidationEvent; import org.keycloak.models.cache.infinispan.events.UserUpdatedEvent; +import org.keycloak.models.utils.ReadOnlyUserModelDelegate; import org.keycloak.storage.StorageId; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.UserStorageProviderModel; @@ -296,36 +297,41 @@ public class UserCacheSession implements UserCache { if (!storageId.isLocal()) { ComponentModel component = realm.getComponent(storageId.getProviderId()); UserStorageProviderModel model = new UserStorageProviderModel(component); - UserStorageProviderModel.CachePolicy policy = model.getCachePolicy(); + // although we do set a timeout, Infinispan has no guarantees when the user will be evicted // its also hard to test stuff boolean invalidate = false; - 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()) { + 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 (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) { + } 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; + } } } } @@ -346,6 +352,14 @@ public class UserCacheSession implements UserCache { if (!storageId.isLocal()) { ComponentModel component = realm.getComponent(storageId.getProviderId()); UserStorageProviderModel model = new UserStorageProviderModel(component); + if (!model.isEnabled()) { + return new ReadOnlyUserModelDelegate(delegate) { + @Override + public boolean isEnabled() { + return false; + } + }; + } UserStorageProviderModel.CachePolicy policy = model.getCachePolicy(); if (policy != null && policy == UserStorageProviderModel.CachePolicy.NO_CACHE) { return delegate; @@ -845,7 +859,7 @@ public class UserCacheSession implements UserCache { @Override public boolean removeUser(RealmModel realm, UserModel user) { - fullyInvalidateUser(realm, user); + fullyInvalidateUser(realm, user); return getDelegate().removeUser(realm, user); } diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java index 55de181aa7..1ec06a63e6 100755 --- a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java +++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderModel.java @@ -38,6 +38,7 @@ public class UserStorageProviderModel extends PrioritizedComponentModel { public static final String FULL_SYNC_PERIOD = "fullSyncPeriod"; public static final String CHANGED_SYNC_PERIOD = "changedSyncPeriod"; public static final String LAST_SYNC = "lastSync"; + public static final String ENABLED = "enabled"; public static enum CachePolicy { NO_CACHE, @@ -59,6 +60,7 @@ public class UserStorageProviderModel extends PrioritizedComponentModel { private transient Integer changedSyncPeriod; private transient Integer lastSync; private transient Boolean importEnabled; + private transient Boolean enabled; private transient CachePolicy cachePolicy; private transient long maxLifespan = -1; private transient int evictionHour = -1; @@ -171,13 +173,30 @@ public class UserStorageProviderModel extends PrioritizedComponentModel { } - - public void setImportEnabled(boolean flag) { importEnabled = 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() { if (fullSyncPeriod == null) { String val = getConfig().getFirst(FULL_SYNC_PERIOD); diff --git a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java index 06bc256979..9517b7241b 100755 --- a/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java +++ b/server-spi/src/main/java/org/keycloak/storage/UserStorageProviderSpi.java @@ -55,6 +55,8 @@ public class UserStorageProviderSpi implements Spi { static { List config = ProviderConfigurationBuilder.create() + .property() + .name("enabled").type(ProviderConfigProperty.BOOLEAN_TYPE).add() .property() .name("priority").type(ProviderConfigProperty.STRING_TYPE).add() .property() diff --git a/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java b/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java index 0dd4aa7019..16f789bfe1 100644 --- a/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java +++ b/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java @@ -30,6 +30,7 @@ import org.keycloak.storage.UserStorageManager; import org.keycloak.storage.UserStorageProvider; import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; @@ -49,9 +50,9 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser protected UserCredentialStore getStoreForUser(UserModel user) { if (StorageId.isLocalStorage(user)) { - return (UserCredentialStore)session.userLocalStorage(); + return (UserCredentialStore) session.userLocalStorage(); } else { - return (UserCredentialStore)session.userFederatedStorage(); + return (UserCredentialStore) session.userFederatedStorage(); } } @@ -105,10 +106,11 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser String providerId = StorageId.resolveProviderId(user); UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId); if (provider instanceof CredentialInputValidator) { + if (!UserStorageManager.isStorageProviderEnabled(realm, providerId)) return false; Iterator it = toValidate.iterator(); while (it.hasNext()) { CredentialInput input = it.next(); - CredentialInputValidator validator = (CredentialInputValidator)provider; + CredentialInputValidator validator = (CredentialInputValidator) provider; if (validator.supportsCredentialType(input.getType()) && validator.isValid(realm, user, input)) { it.remove(); } @@ -118,6 +120,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser if (user.getFederationLink() != null) { UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink()); if (provider != null && provider instanceof CredentialInputValidator) { + if (!UserStorageManager.isStorageProviderEnabled(realm, user.getFederationLink())) return false; validate(realm, user, toValidate, ((CredentialInputValidator)provider)); } } @@ -147,7 +150,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser List list = new LinkedList(); for (ProviderFactory f : session.getKeycloakSessionFactory().getProviderFactories(CredentialProvider.class)) { if (!Types.supports(type, f, CredentialProviderFactory.class)) continue; - list.add((T)session.getProvider(CredentialProvider.class, f.getId())); + list.add((T) session.getProvider(CredentialProvider.class, f.getId())); } return list; @@ -159,16 +162,19 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser String providerId = StorageId.resolveProviderId(user); UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId); if (provider instanceof CredentialInputUpdater) { - CredentialInputUpdater updater = (CredentialInputUpdater)provider; + if (!UserStorageManager.isStorageProviderEnabled(realm, providerId)) return; + CredentialInputUpdater updater = (CredentialInputUpdater) provider; if (updater.supportsCredentialType(input.getType())) { if (updater.updateCredential(realm, user, input)) return; } + } } else { if (user.getFederationLink() != null) { UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink()); if (provider != null && provider instanceof CredentialInputUpdater) { - if (((CredentialInputUpdater)provider).updateCredential(realm, user, input)) return; + if (!UserStorageManager.isStorageProviderEnabled(realm, user.getFederationLink())) return; + if (((CredentialInputUpdater) provider).updateCredential(realm, user, input)) return; } } } @@ -180,22 +186,26 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser } } + @Override public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) { if (!StorageId.isLocalStorage(user)) { String providerId = StorageId.resolveProviderId(user); UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId); if (provider instanceof CredentialInputUpdater) { - CredentialInputUpdater updater = (CredentialInputUpdater)provider; + if (!UserStorageManager.isStorageProviderEnabled(realm, providerId)) return; + CredentialInputUpdater updater = (CredentialInputUpdater) provider; if (updater.supportsCredentialType(credentialType)) { updater.disableCredentialType(realm, user, credentialType); } + } } else { if (user.getFederationLink() != null) { UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink()); if (provider != null && provider instanceof CredentialInputUpdater) { - ((CredentialInputUpdater)provider).disableCredentialType(realm, user, credentialType); + if (!UserStorageManager.isStorageProviderEnabled(realm, user.getFederationLink())) return; + ((CredentialInputUpdater) provider).disableCredentialType(realm, user, credentialType); } } @@ -218,14 +228,16 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser String providerId = StorageId.resolveProviderId(user); UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId); if (provider instanceof CredentialInputUpdater) { - CredentialInputUpdater updater = (CredentialInputUpdater)provider; + if (!UserStorageManager.isStorageProviderEnabled(realm, providerId)) return Collections.EMPTY_SET; + CredentialInputUpdater updater = (CredentialInputUpdater) provider; types.addAll(updater.getDisableableCredentialTypes(realm, user)); } } else { if (user.getFederationLink() != null) { UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink()); if (provider != null && provider instanceof CredentialInputUpdater) { - types.addAll(((CredentialInputUpdater)provider).getDisableableCredentialTypes(realm, user)); + if (!UserStorageManager.isStorageProviderEnabled(realm, user.getFederationLink())) return Collections.EMPTY_SET; + types.addAll(((CredentialInputUpdater) provider).getDisableableCredentialTypes(realm, user)); } } @@ -244,7 +256,8 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser String providerId = StorageId.resolveProviderId(user); UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId); if (provider instanceof CredentialInputValidator) { - CredentialInputValidator validator = (CredentialInputValidator)provider; + if (!UserStorageManager.isStorageProviderEnabled(realm, providerId)) return false; + CredentialInputValidator validator = (CredentialInputValidator) provider; if (validator.supportsCredentialType(type) && validator.isConfiguredFor(realm, user, type)) { return true; } @@ -253,7 +266,8 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser if (user.getFederationLink() != null) { UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, user.getFederationLink()); if (provider != null && provider instanceof CredentialInputValidator) { - if (((CredentialInputValidator)provider).isConfiguredFor(realm, user, type)) return true; + if (!UserStorageManager.isStorageProviderEnabled(realm, user.getFederationLink())) return false; + if (((CredentialInputValidator) provider).isConfiguredFor(realm, user, type)) return true; } } @@ -276,7 +290,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser @Override public CredentialValidationOutput authenticate(KeycloakSession session, RealmModel realm, CredentialInput input) { - List list = UserStorageManager.getStorageProviders(session, realm, CredentialAuthentication.class); + List list = UserStorageManager.getEnabledStorageProviders(session, realm, CredentialAuthentication.class); for (CredentialAuthentication auth : list) { if (auth.supportsCredentialAuthenticationFor(input.getType())) { CredentialValidationOutput output = auth.authenticate(realm, input); diff --git a/services/src/main/java/org/keycloak/storage/UserStorageManager.java b/services/src/main/java/org/keycloak/storage/UserStorageManager.java index 7cbe67c901..16d14e08f1 100755 --- a/services/src/main/java/org/keycloak/storage/UserStorageManager.java +++ b/services/src/main/java/org/keycloak/storage/UserStorageManager.java @@ -38,6 +38,7 @@ import org.keycloak.models.cache.CachedUserModel; import org.keycloak.models.cache.OnUserCache; import org.keycloak.models.cache.UserCache; import org.keycloak.models.utils.ComponentUtil; +import org.keycloak.models.utils.ReadOnlyUserModelDelegate; import org.keycloak.services.managers.UserStorageSyncManager; import org.keycloak.storage.federated.UserFederatedStorageProvider; import org.keycloak.storage.user.ImportedUserValidation; @@ -70,6 +71,11 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo this.session = session; } + public static boolean isStorageProviderEnabled(RealmModel realm, String providerId) { + UserStorageProviderModel model = getStorageProviderModel(realm, providerId); + return model.isEnabled(); + } + protected UserProvider localStorage() { return session.userLocalStorage(); } @@ -109,6 +115,25 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo } + public static List getEnabledStorageProviders(KeycloakSession session, RealmModel realm, Class type) { + List list = new LinkedList<>(); + for (UserStorageProviderModel model : getStorageProviders(realm)) { + if (!model.isEnabled()) continue; + UserStorageProviderFactory factory = (UserStorageProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId()); + if (factory == null) { + logger.warnv("Configured UserStorageProvider {0} of provider id {1} does not exist in realm {2}", model.getName(), model.getProviderId(), realm.getName()); + continue; + } + if (Types.supports(type, factory, UserStorageProviderFactory.class)) { + list.add(type.cast(getStorageProviderInstance(session, model, factory))); + } + + + } + return list; + } + + @Override public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles, boolean addDefaultRequiredActions) { return localStorage().addUser(realm, id, username.toLowerCase(), addDefaultRoles, addDefaultRequiredActions); @@ -116,7 +141,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo @Override public UserModel addUser(RealmModel realm, String username) { - for (UserRegistrationProvider provider : getStorageProviders(session, realm, UserRegistrationProvider.class)) { + for (UserRegistrationProvider provider : getEnabledStorageProviders(session, realm, UserRegistrationProvider.class)) { UserModel user = provider.addUser(realm, username); if (user != null) return user; } @@ -124,14 +149,21 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo return localStorage().addUser(realm, username.toLowerCase()); } + public static UserStorageProviderModel getStorageProviderModel(RealmModel realm, String componentId) { + ComponentModel model = realm.getComponent(componentId); + if (model == null) return null; + return new UserStorageProviderModel(model); + } + public static UserStorageProvider getStorageProvider(KeycloakSession session, RealmModel realm, String componentId) { ComponentModel model = realm.getComponent(componentId); if (model == null) return null; + UserStorageProviderModel storageModel = new UserStorageProviderModel(model); UserStorageProviderFactory factory = (UserStorageProviderFactory)session.getKeycloakSessionFactory().getProviderFactory(UserStorageProvider.class, model.getProviderId()); if (factory == null) { throw new ModelException("Could not find UserStorageProviderFactory for: " + model.getProviderId()); } - return getStorageProviderInstance(session, new UserStorageProviderModel(model), factory); + return getStorageProviderInstance(session, storageModel, factory); } @Override @@ -139,13 +171,18 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo if (getFederatedStorage() != null) getFederatedStorage().preRemove(realm, user); StorageId storageId = new StorageId(user.getId()); if (storageId.getProviderId() == null) { + boolean linkRemoved = true; if (user.getFederationLink() != null) { - UserStorageProvider provider = getStorageProvider(session, realm, user.getFederationLink()); - if (provider != null && provider instanceof UserRegistrationProvider) { - ((UserRegistrationProvider)provider).removeUser(realm, user); + if (isStorageProviderEnabled(realm, user.getFederationLink())) { + UserStorageProvider provider = getStorageProvider(session, realm, user.getFederationLink()); + if (provider != null && provider instanceof UserRegistrationProvider) { + ((UserRegistrationProvider) provider).removeUser(realm, user); + } + } else { + linkRemoved = false; } } - return localStorage().removeUser(realm, user); + return localStorage().removeUser(realm, user) && linkRemoved; } UserRegistrationProvider registry = (UserRegistrationProvider)getStorageProvider(session, realm, storageId.getProviderId()); if (registry == null) { @@ -264,6 +301,14 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo if (user == null || user.getFederationLink() == null) return user; UserStorageProvider provider = getStorageProvider(session, realm, user.getFederationLink()); if (provider != null && provider instanceof ImportedUserValidation) { + if (!isStorageProviderEnabled(realm, user.getFederationLink())) { + return new ReadOnlyUserModelDelegate(user) { + @Override + public boolean isEnabled() { + return false; + } + }; + } UserModel validated = ((ImportedUserValidation) provider).validate(realm, user); if (validated == null) { deleteInvalidUser(realm, user); @@ -324,6 +369,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo } UserLookupProvider provider = (UserLookupProvider)getStorageProvider(session, realm, storageId.getProviderId()); if (provider == null) return null; + if (!isStorageProviderEnabled(realm, storageId.getProviderId())) return null; return provider.getUserById(id, realm); } @@ -343,7 +389,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo if (user != null) { return importValidation(realm, user); } - for (UserLookupProvider provider : getStorageProviders(session, realm, UserLookupProvider.class)) { + for (UserLookupProvider provider : getEnabledStorageProviders(session, realm, UserLookupProvider.class)) { user = provider.getUserByUsername(username, realm); if (user != null) return user; } @@ -356,7 +402,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo if (user != null) { return importValidation(realm, user); } - for (UserLookupProvider provider : getStorageProviders(session, realm, UserLookupProvider.class)) { + for (UserLookupProvider provider : getEnabledStorageProviders(session, realm, UserLookupProvider.class)) { user = provider.getUserByEmail(email, realm); if (user != null) { return user; @@ -401,7 +447,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo @Override public int getUsersCount(RealmModel realm, boolean includeServiceAccount) { int size = localStorage().getUsersCount(realm, includeServiceAccount); - for (UserQueryProvider provider : getStorageProviders(session, realm, UserQueryProvider.class)) { + for (UserQueryProvider provider : getEnabledStorageProviders(session, realm, UserQueryProvider.class)) { size += provider.getUsersCount(realm); } return size; @@ -422,7 +468,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo if (firstResult < 0) firstResult = 0; if (maxResults < 0) maxResults = Integer.MAX_VALUE - 1; - List storageProviders = getStorageProviders(session, realm, UserQueryProvider.class); + List storageProviders = getEnabledStorageProviders(session, realm, UserQueryProvider.class); // we can skip rest of method if there are no storage providers if (storageProviders.isEmpty()) { return pagedQuery.query(localStorage(), firstResult, maxResults); @@ -560,7 +606,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo @Override public void grantToAllUsers(RealmModel realm, RoleModel role) { - List storageProviders = getStorageProviders(session, realm, UserBulkUpdateProvider.class); + List storageProviders = getEnabledStorageProviders(session, realm, UserBulkUpdateProvider.class); LinkedList providers = new LinkedList<>(); providers.add(localStorage()); providers.addAll(storageProviders); @@ -607,7 +653,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo localStorage().preRemove(realm); if (getFederatedStorage() != null) { getFederatedStorage().preRemove(realm); - for (UserStorageProvider provider : getStorageProviders(session, realm, UserStorageProvider.class)) { + for (UserStorageProvider provider : getEnabledStorageProviders(session, realm, UserStorageProvider.class)) { provider.preRemove(realm); } } @@ -618,7 +664,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo localStorage().preRemove(realm, group); if (getFederatedStorage() != null) { getFederatedStorage().preRemove(realm, group); - for (UserStorageProvider provider : getStorageProviders(session, realm, UserStorageProvider.class)) { + for (UserStorageProvider provider : getEnabledStorageProviders(session, realm, UserStorageProvider.class)) { provider.preRemove(realm, group); } } @@ -629,7 +675,7 @@ public class UserStorageManager implements UserProvider, OnUserCache, OnCreateCo localStorage().preRemove(realm, role); if (getFederatedStorage() != null) { getFederatedStorage().preRemove(realm, role); - for (UserStorageProvider provider : getStorageProviders(session, realm, UserStorageProvider.class)) { + for (UserStorageProvider provider : getEnabledStorageProviders(session, realm, UserStorageProvider.class)) { provider.preRemove(realm, role); } } diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/FailableHardcodedStorageProvider.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/FailableHardcodedStorageProvider.java index 780d1c7e13..9ce2827874 100644 --- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/FailableHardcodedStorageProvider.java +++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/FailableHardcodedStorageProvider.java @@ -22,6 +22,7 @@ import org.keycloak.credential.CredentialInput; import org.keycloak.credential.CredentialInputUpdater; import org.keycloak.credential.CredentialInputValidator; import org.keycloak.credential.CredentialModel; +import org.keycloak.models.GroupModel; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; @@ -30,9 +31,11 @@ import org.keycloak.models.utils.UserModelDelegate; import org.keycloak.storage.UserStorageProvider; import org.keycloak.storage.user.ImportedUserValidation; import org.keycloak.storage.user.UserLookupProvider; +import org.keycloak.storage.user.UserQueryProvider; import java.util.Collections; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -41,7 +44,7 @@ import java.util.Set; * @author Bill Burke * @version $Revision: 1 $ */ -public class FailableHardcodedStorageProvider implements UserStorageProvider, UserLookupProvider, ImportedUserValidation, CredentialInputUpdater, CredentialInputValidator { +public class FailableHardcodedStorageProvider implements UserStorageProvider, UserLookupProvider, UserQueryProvider, ImportedUserValidation, CredentialInputUpdater, CredentialInputValidator { public static String username = "billb"; public static String password = "password"; @@ -64,13 +67,13 @@ public class FailableHardcodedStorageProvider implements UserStorageProvider, Us @Override public boolean supportsCredentialType(String credentialType) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); return CredentialModel.PASSWORD.equals(credentialType); } @Override public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); if (!(input instanceof UserCredentialModel)) return false; if (!user.getUsername().equals(username)) throw new RuntimeException("UNKNOWN USER!"); @@ -85,25 +88,25 @@ public class FailableHardcodedStorageProvider implements UserStorageProvider, Us @Override public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); } @Override public Set getDisableableCredentialTypes(RealmModel realm, UserModel user) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); return Collections.EMPTY_SET; } @Override public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); return CredentialModel.PASSWORD.equals(credentialType); } @Override public boolean isValid(RealmModel realm, UserModel user, CredentialInput input) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); if (!(input instanceof UserCredentialModel)) return false; if (!user.getUsername().equals("billb")) throw new RuntimeException("UNKNOWN USER!"); if (input.getType().equals(UserCredentialModel.PASSWORD)) { @@ -163,19 +166,19 @@ public class FailableHardcodedStorageProvider implements UserStorageProvider, Us @Override public UserModel validate(RealmModel realm, UserModel user) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); return new Delegate(user); } @Override public UserModel getUserById(String id, RealmModel realm) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); throw new RuntimeException("THIS IMPORTS SHOULD NEVER BE CALLED"); } @Override public UserModel getUserByUsername(String uname, RealmModel realm) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); if (!username.equals(uname)) return null; UserModel local = session.userLocalStorage().getUserByUsername(uname, realm); if (local != null && !model.getId().equals(local.getFederationLink())) { @@ -198,10 +201,96 @@ public class FailableHardcodedStorageProvider implements UserStorageProvider, Us @Override public UserModel getUserByEmail(String email, RealmModel realm) { - if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + checkForceFail(); return null; } + protected void checkForceFail() { + if (fail || componentFail) throw new RuntimeException("FORCED FAILURE"); + } + + @Override + public int getUsersCount(RealmModel realm) { + checkForceFail(); + return 1; + } + + @Override + public List getUsers(RealmModel realm) { + checkForceFail(); + UserModel hardcoded = getUserByUsername(username, realm); + List list = new LinkedList<>(); + list.add(hardcoded); + return list; + } + + @Override + public List getUsers(RealmModel realm, int firstResult, int maxResults) { + checkForceFail(); + UserModel hardcoded = getUserByUsername(username, realm); + List list = new LinkedList<>(); + list.add(hardcoded); + return list; + } + + @Override + public List searchForUser(String search, RealmModel realm) { + checkForceFail(); + if (!search.equals(username)) return Collections.EMPTY_LIST; + UserModel hardcoded = getUserByUsername(username, realm); + List list = new LinkedList<>(); + list.add(hardcoded); + return list; + } + + @Override + public List searchForUser(String search, RealmModel realm, int firstResult, int maxResults) { + checkForceFail(); + if (!search.equals(username)) return Collections.EMPTY_LIST; + UserModel hardcoded = getUserByUsername(username, realm); + List list = new LinkedList<>(); + list.add(hardcoded); + return list; + } + + @Override + public List searchForUser(Map params, RealmModel realm) { + checkForceFail(); + if (!username.equals(params.get("username")))return Collections.EMPTY_LIST; + UserModel hardcoded = getUserByUsername(username, realm); + List list = new LinkedList<>(); + list.add(hardcoded); + return list; + } + + @Override + public List searchForUser(Map params, RealmModel realm, int firstResult, int maxResults) { + checkForceFail(); + if (!username.equals(params.get("username")))return Collections.EMPTY_LIST; + UserModel hardcoded = getUserByUsername(username, realm); + List list = new LinkedList<>(); + list.add(hardcoded); + return list; + } + + @Override + public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + checkForceFail(); + return Collections.EMPTY_LIST; + } + + @Override + public List getGroupMembers(RealmModel realm, GroupModel group) { + checkForceFail(); + return Collections.EMPTY_LIST; + } + + @Override + public List searchForUserByUserAttribute(String attrName, String attrValue, RealmModel realm) { + checkForceFail(); + return Collections.EMPTY_LIST; + } + @Override public void close() { diff --git a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java index d8d5fbd87a..6a1abfac86 100644 --- a/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java +++ b/testsuite/integration-deprecated/src/test/java/org/keycloak/testsuite/federation/storage/UserStorageFailureTest.java @@ -16,6 +16,7 @@ */ package org.keycloak.testsuite.federation.storage; +import freemarker.ext.beans.HashAdapter; import org.junit.After; import org.junit.Assert; import org.junit.ClassRule; @@ -59,8 +60,10 @@ import org.keycloak.testsuite.rule.WebRule; import org.openqa.selenium.WebDriver; import java.util.Calendar; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -69,6 +72,8 @@ import java.util.Set; */ public class UserStorageFailureTest { public static ComponentModel memoryProvider = null; + public static String realmName; + public static final String LOCAL_USER = "localUser"; @ClassRule public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() { @@ -80,6 +85,7 @@ public class UserStorageFailureTest { model.setProviderId(FailableHardcodedStorageProviderFactory.PROVIDER_ID); model.setParentId(appRealm.getId()); memoryProvider = appRealm.addComponentModel(model); + realmName = appRealm.getName(); ClientModel offlineClient = appRealm.addClient("offline-client"); offlineClient.setEnabled(true); @@ -98,6 +104,10 @@ public class UserStorageFailureTest { serviceAccount.grantRole(role); serviceAccount.setServiceAccountClientLink(offlineClient.getClientId()); + UserModel localUser = manager.getSession().userLocalStorage().addUser(appRealm, LOCAL_USER); + localUser.setEnabled(true); + + } }); @@ -131,7 +141,7 @@ public class UserStorageFailureTest { oauth.scope(OAuth2Constants.OFFLINE_ACCESS); oauth.clientId("offline-client"); oauth.redirectUri(Constants.AUTH_SERVER_ROOT + "/offline-client"); - oauth.doLogin("billb", "password"); + oauth.doLogin(FailableHardcodedStorageProvider.username, "password"); Event loginEvent = events.expectLogin() .client("offline-client") @@ -149,18 +159,215 @@ public class UserStorageFailureTest { RefreshToken offlineToken = oauth.verifyRefreshToken(offlineTokenString); events.clear(); - FailableHardcodedStorageProvider.fail = true; + evictUser(FailableHardcodedStorageProvider.username); + + KeycloakSession session; + RealmModel realm; + UserModel user; + + toggleForceFail(true); + + // make sure failure is turned on + session = keycloakRule.startSession(); + realm = session.realms().getRealmByName(realmName); + try { + user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); + Assert.fail(); + } catch (Exception e) { + Assert.assertEquals("FORCED FAILURE", e.getMessage()); + + } + keycloakRule.stopSession(session, false); + // restart server to make sure we can still boot if user storage is down keycloakRule.restartServer(); + toggleForceFail(false); + + // test that once user storage provider is available again we can still access the token. - FailableHardcodedStorageProvider.fail = false; tokenResponse = oauth.doRefreshTokenRequest(offlineTokenString, "secret"); Assert.assertNotNull(tokenResponse.getAccessToken()); token = oauth.verifyToken(tokenResponse.getAccessToken()); offlineTokenString = tokenResponse.getRefreshToken(); offlineToken = oauth.verifyRefreshToken(offlineTokenString); events.clear(); + + + } + + protected void evictUser(String username) { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName(realmName); + UserModel user = session.users().getUserByUsername(username, realm); + session.userCache().evict(realm, user); + keycloakRule.stopSession(session, true); + } + + protected void toggleForceFail(boolean toggle) { + KeycloakSession session; + RealmModel realm; + session = keycloakRule.startSession(); + memoryProvider.getConfig().putSingle("fail", Boolean.toString(toggle)); + realm = session.realms().getRealmByName(realmName); + realm.updateComponent(memoryProvider); + keycloakRule.stopSession(session, true); + } + + protected void toggleProviderEnabled(boolean toggle) { + KeycloakSession session; + RealmModel realm; + session = keycloakRule.startSession(); + UserStorageProviderModel model = new UserStorageProviderModel(memoryProvider); + model.setEnabled(toggle); + realm = session.realms().getRealmByName(realmName); + realm.updateComponent(model); + keycloakRule.stopSession(session, true); + } + + private void loginSuccessAndLogout(String username, String password) { + loginPage.open(); + loginPage.login(username, password); + Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType()); + Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); + oauth.openLogout(); + } + + @Test + public void testKeycloak5926() { + // make sure local copy is deleted + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName(realmName); + UserModel user = session.userLocalStorage().getUserByUsername(FailableHardcodedStorageProvider.username, realm); + if (user != null) { + session.userLocalStorage().removeUser(realm, user); + } + keycloakRule.stopSession(session, true); + } + + // query user to make sure its imported + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName(realmName); + UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); + Assert.assertNotNull(user); + keycloakRule.stopSession(session, true); + } + + + + evictUser(FailableHardcodedStorageProvider.username); + evictUser(LOCAL_USER); + + toggleForceFail(true); + { + KeycloakSession session = keycloakRule.startSession(); + // make sure we can still query local users + RealmModel realm = session.realms().getRealmByName(realmName); + UserModel local = session.users().getUserByUsername(LOCAL_USER, realm); + Assert.assertNotNull(local); + // assert that lookup of user storage user fails + try { + UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); + Assert.fail(); + } catch (Exception e) { + Assert.assertEquals("FORCED FAILURE", e.getMessage()); + + } + + keycloakRule.stopSession(session, false); + } + // test that we can still login to a user + loginSuccessAndLogout("test-user@localhost", "password"); + + toggleProviderEnabled(false); + { + KeycloakSession session = keycloakRule.startSession(); + // make sure we can still query local users + RealmModel realm = session.realms().getRealmByName(realmName); + UserModel local = session.users().getUserByUsername(LOCAL_USER, realm); + Assert.assertNotNull(local); + List result; + result = session.users().searchForUser(LOCAL_USER, realm); + Assert.assertEquals(1, result.size()); + session.users().searchForUser(FailableHardcodedStorageProvider.username, realm); + Assert.assertEquals(1, result.size()); + session.users().searchForUser(LOCAL_USER, realm, 0, 2); + Assert.assertEquals(1, result.size()); + session.users().searchForUser(FailableHardcodedStorageProvider.username, realm, 0, 2); + Assert.assertEquals(1, result.size()); + Map localParam = new HashMap<>(); + localParam.put("username", LOCAL_USER); + Map hardcodedParam = new HashMap<>(); + hardcodedParam.put("username", FailableHardcodedStorageProvider.username); + + result = session.users().searchForUser(localParam, realm); + Assert.assertEquals(1, result.size()); + session.users().searchForUser(hardcodedParam, realm); + Assert.assertEquals(1, result.size()); + session.users().searchForUser(localParam, realm, 0, 2); + Assert.assertEquals(1, result.size()); + session.users().searchForUser(hardcodedParam, realm, 0, 2); + Assert.assertEquals(1, result.size()); + + session.users().getUsers(realm); + session.users().getUsersCount(realm); + + + + UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); + Assert.assertFalse(user instanceof CachedUserModel); + Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername()); + Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail()); + Assert.assertFalse(user.isEnabled()); + try { + user.setEmail("error@error.com"); + Assert.fail(); + } catch (Exception ex) { + + } + keycloakRule.stopSession(session, true); + } + // make sure user isn't cached as provider is disabled + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName(realmName); + UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); + Assert.assertFalse(user instanceof CachedUserModel); + Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername()); + Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail()); + keycloakRule.stopSession(session, true); + } + + // make ABSOLUTELY sure user isn't cached as provider is disabled + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName(realmName); + UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); + Assert.assertFalse(user instanceof CachedUserModel); + Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername()); + Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail()); + keycloakRule.stopSession(session, true); + } + + + + toggleProviderEnabled(true); + toggleForceFail(false); + + // user should be cachable now + { + KeycloakSession session = keycloakRule.startSession(); + RealmModel realm = session.realms().getRealmByName(realmName); + UserModel user = session.users().getUserByUsername(FailableHardcodedStorageProvider.username, realm); + Assert.assertTrue(user instanceof CachedUserModel); + Assert.assertEquals(FailableHardcodedStorageProvider.username, user.getUsername()); + Assert.assertEquals(FailableHardcodedStorageProvider.email, user.getEmail()); + keycloakRule.stopSession(session, true); + } + + events.clear(); } @After diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index 66b194305e..d76a6f1091 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -748,6 +748,7 @@ console-display-name=Console Display Name console-display-name.tooltip=Display name of provider when linked in admin console. priority=Priority priority.tooltip=Priority of provider when doing a user lookup. Lowest first. +user-storage.enabled.tooltip=If provider is disabled it will not be considered for queries and imported users will be disabled and read only until the provider is enabled again. sync-settings=Sync Settings periodic-full-sync=Periodic Full Sync periodic-full-sync.tooltip=Does periodic full synchronization of provider users to Keycloak should be enabled or not diff --git a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js index c50b691165..f1c07425bc 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js @@ -648,6 +648,10 @@ module.controller('UserFederationCtrl', function($scope, $location, $route, real return instance.providerId; } + $scope.isProviderEnabled = function(instance) { + return !instance.config['enabled'] || instance.config['enabled'][0] == 'true'; + } + $scope.getInstancePriority = function(instance) { if (!instance.config['priority']) { console.log('getInstancePriority is undefined'); @@ -711,6 +715,7 @@ module.controller('GenericUserStorageCtrl', function($scope, $location, Notifica }; instance.config['priority'] = ["0"]; + instance.config['enabled'] = ["true"]; $scope.fullSyncEnabled = false; $scope.changedSyncEnabled = false; @@ -753,6 +758,9 @@ module.controller('GenericUserStorageCtrl', function($scope, $location, Notifica } } + if (!instance.config['enabled']) { + instance.config['enabled'] = ['true']; + } if (!instance.config['cachePolicy']) { instance.config['cachePolicy'] = ['DEFAULT']; @@ -1061,6 +1069,7 @@ module.controller('LDAPUserStorageCtrl', function($scope, $location, Notificatio instance.config = { }; + instance.config['enabled'] = ["true"]; instance.config['priority'] = ["0"]; $scope.fullSyncEnabled = false; @@ -1098,6 +1107,9 @@ module.controller('LDAPUserStorageCtrl', function($scope, $location, Notificatio instance.config['fullSyncPeriod'] = ['-1']; } + if (!instance.config['enabled']) { + instance.config['enabled'] = ['true']; + } if (!instance.config['changedSyncPeriod']) { console.log('setting to -1'); instance.config['changedSyncPeriod'] = ['-1']; diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html index 8298a999da..3b97f519f3 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-federation.html @@ -44,6 +44,7 @@ {{:: 'id' | translate}} + {{:: 'enabled' | translate}} {{:: 'provider-name' | translate}} {{:: 'priority' | translate}} {{:: 'actions' | translate}} @@ -52,6 +53,7 @@ {{getInstanceName(instance)}} + {{isProviderEnabled(instance)}} {{getInstanceProvider(instance) | capitalize}} {{getInstancePriority(instance)}} {{:: 'edit' | translate}} diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-generic.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-generic.html index b4a1fda3a1..2b2456c883 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-generic.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-generic.html @@ -16,6 +16,13 @@ +
+ +
+ +
+ {{:: 'user-storage.enabled.tooltip' | translate}} +
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-kerberos.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-kerberos.html index 5dd0a67031..b7fa6efe05 100644 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-kerberos.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-kerberos.html @@ -16,6 +16,13 @@
+
+ +
+ +
+ {{:: 'user-storage.enabled.tooltip' | translate}} +
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-ldap.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-ldap.html index 941283938c..0a391b9274 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-ldap.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage-ldap.html @@ -19,6 +19,13 @@
+
+ +
+ +
+ {{:: 'user-storage.enabled.tooltip' | translate}} +
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage.html index aaf2158291..93ca5a9b6a 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/user-storage.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-storage.html @@ -21,6 +21,7 @@ {{:: 'id' | translate}} {{:: 'provider-name' | translate}} + {{:: 'enabled' | translate}} {{:: 'priority' | translate}} {{:: 'actions' | translate}} @@ -29,6 +30,7 @@ {{instance.name}} {{instance.providerId|capitalize}} + {{instance.config['enabled'][0]}} {{instance.config['priority'][0]}} {{:: 'edit' | translate}} {{:: 'delete' | translate}}