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 c36f002122..16987d493d 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
@@ -637,11 +637,6 @@ public class UserCacheSession implements UserCache {
return getDelegate().removeFederatedIdentity(realm, user, socialProvider);
}
- @Override
- public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
- return getDelegate().validCredentials(session, realm, input);
- }
-
@Override
public void grantToAllUsers(RealmModel realm, RoleModel role) {
realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index e03b736423..6f85e38d0f 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -707,12 +707,6 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
return (entity != null) ? new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName(), entity.getToken()) : null;
}
- @Override
- public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
- // Not supported yet
- return null;
- }
-
@Override
public void preRemove(RealmModel realm, ComponentModel component) {
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index 96a9d316b5..529aabe899 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -517,12 +517,6 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore {
getMongoStore().updateEntities(MongoUserConsentEntity.class, query, pull, invocationContext);
}
- @Override
- public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
- // Not supported yet
- return null;
- }
-
@Override
public void addConsent(RealmModel realm, UserModel user, UserConsentModel consent) {
String clientId = consent.getClient().getId();
diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserCredentialAuthenticationProvider.java b/server-spi/src/main/java/org/keycloak/credential/CredentialAuthentication.java
similarity index 66%
rename from server-spi/src/main/java/org/keycloak/storage/user/UserCredentialAuthenticationProvider.java
rename to server-spi/src/main/java/org/keycloak/credential/CredentialAuthentication.java
index 948ef4738b..0acbb71818 100644
--- a/server-spi/src/main/java/org/keycloak/storage/user/UserCredentialAuthenticationProvider.java
+++ b/server-spi/src/main/java/org/keycloak/credential/CredentialAuthentication.java
@@ -14,20 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.keycloak.storage.user;
+package org.keycloak.credential;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserCredentialModel;
import java.util.Set;
/**
+ * Single purpose method that knows how to authenticate a user based on a credential type. This is used when the user
+ * is not known but the provider knows how to extract this information from the credential. Examples are Kerberos.
+ *
* @author Bill Burke
* @version $Revision: 1 $
*/
-public interface UserCredentialAuthenticationProvider {
- Set getSupportedCredentialAuthenticationTypes();
- CredentialValidationOutput validCredential(KeycloakSession session, RealmModel realm, UserCredentialModel input);
+public interface CredentialAuthentication {
+ boolean supportsCredentialAuthenticationFor(String type);
+ CredentialValidationOutput authenticate(RealmModel realm, CredentialInput input);
}
diff --git a/server-spi/src/main/java/org/keycloak/models/CredentialValidationOutput.java b/server-spi/src/main/java/org/keycloak/models/CredentialValidationOutput.java
index e0faa22152..83694bc528 100644
--- a/server-spi/src/main/java/org/keycloak/models/CredentialValidationOutput.java
+++ b/server-spi/src/main/java/org/keycloak/models/CredentialValidationOutput.java
@@ -49,6 +49,11 @@ public class CredentialValidationOutput {
return authStatus;
}
+ /**
+ * State that is passed back by provider
+ *
+ * @return
+ */
public Map getState() {
return state;
}
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 8dad996608..f03ac8fa36 100644
--- a/server-spi/src/main/java/org/keycloak/models/UserCredentialManager.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserCredentialManager.java
@@ -90,4 +90,17 @@ public interface UserCredentialManager extends UserCredentialStore {
* @return
*/
boolean isConfiguredLocally(RealmModel realm, UserModel user, String type);
+
+ /**
+ * Given a CredentialInput, authenticate the user. This is used in the case where the credential must be processed
+ * to determine and find the user. An example is Kerberos where the kerberos token might be validated and processed
+ * by a variety of different storage providers.
+ *
+ *
+ * @param session
+ * @param realm
+ * @param input
+ * @return
+ */
+ CredentialValidationOutput authenticate(KeycloakSession session, RealmModel realm, CredentialInput input);
}
diff --git a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
index 771a03a979..c722aa4437 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -73,7 +73,7 @@ public class UserFederationManager implements UserProvider {
return user;
}
- protected UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
+ public UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
return KeycloakModelUtils.getFederationProviderInstance(session, model);
}
@@ -484,40 +484,6 @@ public class UserFederationManager implements UserProvider {
session.userStorage().preRemove(protocolMapper);
}
- @Override
- public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
- List fedProviderModels = realm.getUserFederationProviders();
- List fedProviders = new ArrayList();
- for (UserFederationProviderModel fedProviderModel : fedProviderModels) {
- fedProviders.add(getFederationProvider(fedProviderModel));
- }
-
- CredentialValidationOutput result = null;
- for (UserCredentialModel cred : input) {
- UserFederationProvider providerSupportingCreds = null;
-
- // Find first provider, which supports required credential type
- for (UserFederationProvider fedProvider : fedProviders) {
- if (fedProvider.getSupportedCredentialTypes().contains(cred.getType())) {
- providerSupportingCreds = fedProvider;
- break;
- }
- }
-
- if (providerSupportingCreds == null) {
- logger.warn("Don't have provider supporting credentials of type " + cred.getType());
- return CredentialValidationOutput.failed();
- }
-
- logger.debug("Found provider [" + providerSupportingCreds + "] supporting credentials of type " + cred.getType());
- CredentialValidationOutput currentResult = providerSupportingCreds.validCredentials(realm, cred);
- result = (result == null) ? currentResult : result.merge(currentResult);
- }
-
- // For now, validCredentials(realm, input) is not supported for local userProviders
- return (result != null) ? result : CredentialValidationOutput.failed();
- }
-
@Override
public void preRemove(RealmModel realm, ComponentModel component) {
session.userStorage().preRemove(realm, component);
diff --git a/server-spi/src/main/java/org/keycloak/models/UserProvider.java b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
index e1b64fa927..3c796b50db 100755
--- a/server-spi/src/main/java/org/keycloak/models/UserProvider.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserProvider.java
@@ -75,10 +75,6 @@ public interface UserProvider extends Provider,
void preRemove(RealmModel realm, ClientModel client);
void preRemove(ProtocolMapperModel protocolMapper);
-
- CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input);
-
-
void close();
void preRemove(RealmModel realm, ComponentModel component);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
index 1d6e7ae300..c88f49208a 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
@@ -86,7 +86,7 @@ public class SpnegoAuthenticator extends AbstractUsernameFormAuthenticator imple
String spnegoToken = tokens[1];
UserCredentialModel spnegoCredential = UserCredentialModel.kerberos(spnegoToken);
- CredentialValidationOutput output = context.getSession().users().validCredentials(context.getSession(), context.getRealm(), spnegoCredential);
+ CredentialValidationOutput output = context.getSession().userCredentialManager().authenticate(context.getSession(), context.getRealm(), spnegoCredential);
if (output.getAuthStatus() == CredentialValidationOutput.Status.AUTHENTICATED) {
context.setUser(output.getAuthenticatedUser());
diff --git a/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java b/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java
index 065be1fe58..5801efd63d 100644
--- a/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java
+++ b/services/src/main/java/org/keycloak/credential/UserCredentialStoreManager.java
@@ -17,18 +17,23 @@
package org.keycloak.credential;
import org.keycloak.common.util.reflections.Types;
+import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialManager;
+import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProvider;
+import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.OnUserCache;
+import org.keycloak.models.utils.CredentialValidation;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.storage.StorageId;
import org.keycloak.storage.UserStorageManager;
import org.keycloak.storage.UserStorageProvider;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
@@ -246,6 +251,37 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
return false;
}
+ @Override
+ public CredentialValidationOutput authenticate(KeycloakSession session, RealmModel realm, CredentialInput input) {
+ List fedProviderModels = realm.getUserFederationProviders();
+ List fedProviders = new ArrayList();
+ for (UserFederationProviderModel fedProviderModel : fedProviderModels) {
+ UserFederationProvider provider = session.users().getFederationProvider(fedProviderModel);
+ if (input instanceof UserCredentialModel && provider != null && provider.supportsCredentialType(input.getType())) {
+ CredentialValidationOutput output = provider.validCredentials(realm, (UserCredentialModel)input);
+ if (output != null) return output;
+ }
+ }
+
+ List list = UserStorageManager.getStorageProviders(session, realm, CredentialAuthentication.class);
+ for (CredentialAuthentication auth : list) {
+ if (auth.supportsCredentialAuthenticationFor(input.getType())) {
+ CredentialValidationOutput output = auth.authenticate(realm, input);
+ if (output != null) return output;
+ }
+ }
+
+ list = getCredentialProviders(realm, CredentialAuthentication.class);
+ for (CredentialAuthentication auth : list) {
+ if (auth.supportsCredentialAuthenticationFor(input.getType())) {
+ CredentialValidationOutput output = auth.authenticate(realm, input);
+ if (output != null) return output;
+ }
+ }
+
+ return null;
+ }
+
@Override
public void onCache(RealmModel realm, CachedUserModel user, UserModel delegate) {
List credentialProviders = getCredentialProviders(realm, OnUserCache.class);
diff --git a/services/src/main/java/org/keycloak/storage/UserStorageManager.java b/services/src/main/java/org/keycloak/storage/UserStorageManager.java
index 90c8f7264a..6061323727 100755
--- a/services/src/main/java/org/keycloak/storage/UserStorageManager.java
+++ b/services/src/main/java/org/keycloak/storage/UserStorageManager.java
@@ -37,7 +37,7 @@ import org.keycloak.models.UserProvider;
import org.keycloak.models.cache.CachedUserModel;
import org.keycloak.models.cache.OnUserCache;
import org.keycloak.storage.federated.UserFederatedStorageProvider;
-import org.keycloak.storage.user.UserCredentialAuthenticationProvider;
+import org.keycloak.credential.CredentialAuthentication;
import org.keycloak.storage.user.UserLookupProvider;
import org.keycloak.storage.user.UserQueryProvider;
import org.keycloak.storage.user.UserRegistrationProvider;
@@ -528,37 +528,6 @@ public class UserStorageManager implements UserProvider, OnUserCache {
if (getFederatedStorage() != null) getFederatedStorage().preRemove(protocolMapper);
}
- @Override
- public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
- List providers = getStorageProviders(session, realm, UserCredentialAuthenticationProvider.class);
- if (providers.isEmpty()) return CredentialValidationOutput.failed();
-
- CredentialValidationOutput result = null;
- for (UserCredentialModel cred : input) {
- UserCredentialAuthenticationProvider providerSupportingCreds = null;
-
- // Find first provider, which supports required credential type
- for (UserCredentialAuthenticationProvider provider : providers) {
- if (provider.getSupportedCredentialAuthenticationTypes().contains(cred.getType())) {
- providerSupportingCreds = provider;
- break;
- }
- }
-
- if (providerSupportingCreds == null) {
- logger.warn("Don't have provider supporting credentials of type " + cred.getType());
- return CredentialValidationOutput.failed();
- }
-
- logger.debug("Found provider [" + providerSupportingCreds + "] supporting credentials of type " + cred.getType());
- CredentialValidationOutput currentResult = providerSupportingCreds.validCredential(session, realm, cred);
- result = (result == null) ? currentResult : result.merge(currentResult);
- }
-
- // For now, validCredentials(realm, input) is not supported for local userProviders
- return (result != null) ? result : CredentialValidationOutput.failed();
- }
-
@Override
public void preRemove(RealmModel realm, ComponentModel component) {
if (!component.getProviderType().equals(UserStorageProvider.class.getName())) return;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java
index 667c85ff59..61cc47c94b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ScriptAuthenticatorTest.java
@@ -22,20 +22,17 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.keycloak.authentication.AuthenticationFlow;
import org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory;
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationExecutionModel;
-import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
-import org.keycloak.storage.user.UserCredentialAuthenticationProvider;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.util.ExecutionBuilder;
@@ -45,8 +42,6 @@ import org.keycloak.testsuite.util.UserBuilder;
import javax.ws.rs.core.Response;
import java.io.IOException;
-import java.util.Arrays;
-import java.util.Collections;
/**
* Tests for {@link org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticator}