keycloak-uncached
Changes
examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java 17(+16 -1)
examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java 7(+7 -0)
examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java 7(+7 -0)
examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java 13(+13 -0)
federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java 5(+5 -0)
Details
diff --git a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java
index 151fa01..77f3083 100644
--- a/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java
+++ b/examples/providers/authenticator/src/main/java/org/keycloak/examples/authenticator/SecretQuestionCredentialProvider.java
@@ -29,7 +29,10 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.OnUserCache;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -82,12 +85,24 @@ public class SecretQuestionCredentialProvider implements CredentialProvider, Cre
@Override
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
if (!SECRET_QUESTION.equals(credentialType)) return;
- session.userCredentialManager().disableCredential(realm, user, credentialType);
+ session.userCredentialManager().disableCredentialType(realm, user, credentialType);
session.getUserCache().evict(realm, user);
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ if (!session.userCredentialManager().getStoredCredentialsByType(realm, user, SECRET_QUESTION).isEmpty()) {
+ Set<String> set = new HashSet<>();
+ set.add(SECRET_QUESTION);
+ return set;
+ } else {
+ return Collections.EMPTY_SET;
+ }
+
+ }
+
+ @Override
public boolean supportsCredentialType(String credentialType) {
return SECRET_QUESTION.equals(credentialType);
}
diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java
index 2a0597c..19f9f75 100755
--- a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java
+++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/ClasspathPropertiesFederationProvider.java
@@ -23,7 +23,9 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
+import java.util.Collections;
import java.util.Properties;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -90,4 +92,9 @@ public class ClasspathPropertiesFederationProvider extends BasePropertiesFederat
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
}
+
+ @Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ return Collections.EMPTY_SET;
+ }
}
diff --git a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java
index aac080b..f8262f1 100755
--- a/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java
+++ b/examples/providers/federation-provider/src/main/java/org/keycloak/examples/federation/properties/FilePropertiesFederationProvider.java
@@ -25,7 +25,9 @@ import org.keycloak.models.UserModel;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Collections;
import java.util.Properties;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -108,4 +110,9 @@ public class FilePropertiesFederationProvider extends BasePropertiesFederationPr
public void disableCredentialType(RealmModel realm, UserModel user, String credentialType) {
}
+
+ @Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ return Collections.EMPTY_SET;
+ }
}
diff --git a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java
index 8f7f147..837f2b6 100644
--- a/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java
+++ b/examples/providers/user-storage-jpa/src/main/java/org/keycloak/examples/storage/user/EjbExampleUserStorageProvider.java
@@ -44,9 +44,11 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.TypedQuery;
import java.util.Collections;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -200,6 +202,17 @@ public class EjbExampleUserStorageProvider implements UserStorageProvider,
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ if (getUserAdapter(user).getPassword() != null) {
+ Set<String> set = new HashSet<>();
+ set.add(CredentialModel.PASSWORD);
+ return set;
+ } else {
+ return Collections.emptySet();
+ }
+ }
+
+ @Override
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
return supportsCredentialType(credentialType) && getPassword(user) != null;
}
diff --git a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
index 9d82655..d656a0d 100755
--- a/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
+++ b/federation/kerberos/src/main/java/org/keycloak/federation/kerberos/KerberosFederationProvider.java
@@ -165,6 +165,11 @@ public class KerberosFederationProvider implements UserFederationProvider {
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ return Collections.EMPTY_SET;
+ }
+
+ @Override
public boolean supportsCredentialType(String credentialType) {
return credentialType.equals(CredentialModel.KERBEROS) || (kerberosConfig.isAllowPasswordAuthentication() && credentialType.equals(CredentialModel.PASSWORD));
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index 14c99a6..880f0f7 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -425,6 +425,11 @@ public class LDAPFederationProvider implements UserFederationProvider {
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ return Collections.EMPTY_SET;
+ }
+
+ @Override
public boolean supportsCredentialType(String credentialType) {
return getSupportedCredentialTypes().contains(credentialType);
}
diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java
index 8e66943..2820947 100755
--- a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java
+++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java
@@ -181,6 +181,11 @@ public class SSSDFederationProvider implements UserFederationProvider {
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ return Collections.EMPTY_SET;
+ }
+
+ @Override
public boolean supportsCredentialType(String credentialType) {
return CredentialModel.PASSWORD.equals(credentialType);
}
diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialInputUpdater.java b/server-spi/src/main/java/org/keycloak/credential/CredentialInputUpdater.java
index f4e3165..9a0d2b7 100644
--- a/server-spi/src/main/java/org/keycloak/credential/CredentialInputUpdater.java
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialInputUpdater.java
@@ -19,6 +19,9 @@ package org.keycloak.credential;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
+import java.util.List;
+import java.util.Set;
+
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@@ -27,4 +30,14 @@ public interface CredentialInputUpdater {
boolean supportsCredentialType(String credentialType);
boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input);
void disableCredentialType(RealmModel realm, UserModel user, String credentialType);
+
+ /**
+ *
+ * Returns a set of credential types that can be disabled by disableCredentialType() method
+ *
+ * @param realm
+ * @param user
+ * @return
+ */
+ Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user);
}
diff --git a/server-spi/src/main/java/org/keycloak/credential/CredentialInputValidator.java b/server-spi/src/main/java/org/keycloak/credential/CredentialInputValidator.java
index 95e1d1e..0e03bba 100644
--- a/server-spi/src/main/java/org/keycloak/credential/CredentialInputValidator.java
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialInputValidator.java
@@ -19,6 +19,8 @@ package org.keycloak.credential;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
+import java.util.List;
+
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
diff --git a/server-spi/src/main/java/org/keycloak/models/UserCredentialManager.java b/server-spi/src/main/java/org/keycloak/models/UserCredentialManager.java
index f03ac8f..f5e8aab 100644
--- a/server-spi/src/main/java/org/keycloak/models/UserCredentialManager.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserCredentialManager.java
@@ -20,6 +20,7 @@ import org.keycloak.credential.CredentialInput;
import org.keycloak.credential.UserCredentialStore;
import java.util.List;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -67,7 +68,16 @@ public interface UserCredentialManager extends UserCredentialStore {
* @param user
* @param credentialType
*/
- void disableCredential(RealmModel realm, UserModel user, String credentialType);
+ void disableCredentialType(RealmModel realm, UserModel user, String credentialType);
+
+ /**
+ * Returns a set of credential types that can be disabled by disableCredentialType() method
+ *
+ * @param realm
+ * @param user
+ * @return
+ */
+ Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user);
/**
* Checks to see if user has credential type configured. Looks in UserStorageProvider or UserFederationProvider first,
@@ -82,7 +92,8 @@ public interface UserCredentialManager extends UserCredentialStore {
/**
* Only loops through each CredentialProvider to see if credential type is configured for the user.
- * This allows UserStorageProvider and UserFederationProvider to look to abort isValid
+ * This allows UserStorageProvider and UserFederationProvider isValid() implementations to punt to local storage
+ * when validating a credential that has been overriden in Keycloak storage.
*
* @param realm
* @param user
diff --git a/services/src/main/java/org/keycloak/credential/OTPCredentialProvider.java b/services/src/main/java/org/keycloak/credential/OTPCredentialProvider.java
index f5c973a..f71f84a 100644
--- a/services/src/main/java/org/keycloak/credential/OTPCredentialProvider.java
+++ b/services/src/main/java/org/keycloak/credential/OTPCredentialProvider.java
@@ -29,7 +29,9 @@ import org.keycloak.models.utils.HmacOTP;
import org.keycloak.models.utils.TimeBasedOTP;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -140,6 +142,19 @@ public class OTPCredentialProvider implements CredentialProvider, CredentialInpu
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ if (!getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.HOTP).isEmpty()
+ || !getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.TOTP).isEmpty()) {
+ Set<String> set = new HashSet<>();
+ set.add(CredentialModel.OTP);
+ return set;
+ } else {
+ return Collections.EMPTY_SET;
+ }
+ }
+
+
+ @Override
public boolean supportsCredentialType(String credentialType) {
return CredentialModel.OTP.equals(credentialType)
|| CredentialModel.HOTP.equals(credentialType)
diff --git a/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java b/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java
index 4fc5856..d0558ab 100644
--- a/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java
+++ b/services/src/main/java/org/keycloak/credential/PasswordCredentialProvider.java
@@ -33,8 +33,10 @@ import org.keycloak.policy.PolicyError;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -151,6 +153,17 @@ public class PasswordCredentialProvider implements CredentialProvider, Credentia
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ if (!getCredentialStore().getStoredCredentialsByType(realm, user, CredentialModel.PASSWORD).isEmpty()) {
+ Set<String> set = new HashSet<>();
+ set.add(CredentialModel.PASSWORD);
+ return set;
+ } else {
+ return Collections.EMPTY_SET;
+ }
+ }
+
+ @Override
public boolean supportsCredentialType(String credentialType) {
return credentialType.equals(CredentialModel.PASSWORD);
}
diff --git a/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java b/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java
index 9adbae6..a8b4110 100644
--- a/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java
+++ b/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java
@@ -35,9 +35,11 @@ import org.keycloak.storage.UserStorageProvider;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -196,7 +198,7 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
}
}
@Override
- public void disableCredential(RealmModel realm, UserModel user, String credentialType) {
+ 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);
@@ -229,6 +231,38 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
}
+
+ @Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ Set<String> types = new HashSet<>();
+ if (!StorageId.isLocalStorage(user)) {
+ String providerId = StorageId.resolveProviderId(user);
+ UserStorageProvider provider = UserStorageManager.getStorageProvider(session, realm, providerId);
+ if (provider instanceof CredentialInputUpdater) {
+ CredentialInputUpdater updater = (CredentialInputUpdater)provider;
+ types.addAll(updater.getDisableableCredentialTypes(realm, user));
+ }
+ } else {
+ UserFederationProvider link = session.users().getFederationLink(realm, user);
+ if (link != null) {
+ types.addAll(link.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));
+ }
+ }
+
+ }
+
+ List<CredentialInputUpdater> credentialProviders = getCredentialProviders(realm, CredentialInputUpdater.class);
+ for (CredentialInputUpdater updater : credentialProviders) {
+ types.addAll(updater.getDisableableCredentialTypes(realm, user));
+ }
+ return types;
+ }
+
@Override
public boolean isConfiguredFor(RealmModel realm, UserModel user, String type) {
if (!StorageId.isLocalStorage(user)) {
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 2684483..7a50644 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -440,7 +440,7 @@ public class AccountService extends AbstractSecuredLocalService {
csrfCheck(stateChecker);
UserModel user = auth.getUser();
- session.userCredentialManager().disableCredential(realm, user, CredentialModel.OTP);
+ session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP);
event.event(EventType.REMOVE_TOTP).client(auth.getClient()).user(auth.getUser()).success();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index de64bf8..f7c8048 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -719,7 +719,48 @@ public class UsersResource {
}
- /**
+ /**
+ * Disable all credentials for a user of a specific type
+ *
+ * @param id
+ * @param credentialType
+ */
+ @Path("{id}/disable-credential-type/{type}")
+ @DELETE
+ public void disableCredentialType(@PathParam("id") String id, @PathParam("type") String credentialType) {
+ auth.requireManage();
+
+ UserModel user = session.users().getUserById(id, realm);
+ if (user == null) {
+ throw new NotFoundException("User not found");
+ }
+
+ session.userCredentialManager().disableCredentialType(realm, user, credentialType);
+
+ }
+
+ /**
+ * Credential types that are disablable for this user
+ *
+ * @param id
+ * @return
+ */
+ @Path("{id}/disableable-credential-types")
+ @GET
+ @NoCache
+ @Produces(MediaType.APPLICATION_JSON)
+ public Set<String> getCredentialTypes(@PathParam("id") String id) {
+ auth.requireManage();
+
+ UserModel user = session.users().getUserById(id, realm);
+ if (user == null) {
+ throw new NotFoundException("User not found");
+ }
+ return session.userCredentialManager().getDisableableCredentialTypes(realm, user);
+
+ }
+
+ /**
* Set up a temporary password for the user
*
* User will have to reset the temporary password next time they log in.
@@ -777,7 +818,7 @@ public class UsersResource {
throw new NotFoundException("User not found");
}
- session.userCredentialManager().disableCredential(realm, user, CredentialModel.OTP);
+ session.userCredentialManager().disableCredentialType(realm, user, CredentialModel.OTP);
adminEvent.operation(OperationType.ACTION).resourcePath(uriInfo).success();
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorage.java
index 75e12f7..445b350 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/UserMapStorage.java
@@ -33,7 +33,9 @@ import org.keycloak.storage.adapter.AbstractUserAdapterFederatedStorage;
import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserRegistrationProvider;
+import java.util.Collections;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -104,6 +106,11 @@ public class UserMapStorage implements UserLookupProvider, UserStorageProvider,
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ return Collections.EMPTY_SET;
+ }
+
+ @Override
public boolean isConfiguredFor(RealmModel realm, UserModel user, String credentialType) {
return CredentialModel.PASSWORD.equals(credentialType);
}
diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/DummyUserFederationProvider.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/DummyUserFederationProvider.java
index 58493d0..c0dd8d5 100644
--- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/DummyUserFederationProvider.java
+++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/federation/DummyUserFederationProvider.java
@@ -124,6 +124,11 @@ public class DummyUserFederationProvider implements UserFederationProvider {
}
@Override
+ public Set<String> getDisableableCredentialTypes(RealmModel realm, UserModel user) {
+ return Collections.EMPTY_SET;
+ }
+
+ @Override
public boolean supportsCredentialType(String credentialType) {
return getSupportedCredentialTypes().contains(credentialType);
}