Merge pull request #3289 from patriot1burke/master
refactor CredentialValidationOutput apis
This commit is contained in:
commit
42e93742fa
12 changed files with 64 additions and 99 deletions
|
@ -637,11 +637,6 @@ public class UserCacheSession implements UserCache {
|
||||||
return getDelegate().removeFederatedIdentity(realm, user, socialProvider);
|
return getDelegate().removeFederatedIdentity(realm, user, socialProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
|
|
||||||
return getDelegate().validCredentials(session, realm, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void grantToAllUsers(RealmModel realm, RoleModel role) {
|
public void grantToAllUsers(RealmModel realm, RoleModel role) {
|
||||||
realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm
|
realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm
|
||||||
|
|
|
@ -707,12 +707,6 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
return (entity != null) ? new FederatedIdentityModel(entity.getIdentityProvider(), entity.getUserId(), entity.getUserName(), entity.getToken()) : null;
|
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
|
@Override
|
||||||
public void preRemove(RealmModel realm, ComponentModel component) {
|
public void preRemove(RealmModel realm, ComponentModel component) {
|
||||||
|
|
||||||
|
|
|
@ -517,12 +517,6 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore {
|
||||||
getMongoStore().updateEntities(MongoUserConsentEntity.class, query, pull, invocationContext);
|
getMongoStore().updateEntities(MongoUserConsentEntity.class, query, pull, invocationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
|
|
||||||
// Not supported yet
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addConsent(RealmModel realm, UserModel user, UserConsentModel consent) {
|
public void addConsent(RealmModel realm, UserModel user, UserConsentModel consent) {
|
||||||
String clientId = consent.getClient().getId();
|
String clientId = consent.getClient().getId();
|
||||||
|
|
|
@ -14,20 +14,22 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.keycloak.storage.user;
|
package org.keycloak.credential;
|
||||||
|
|
||||||
import org.keycloak.models.CredentialValidationOutput;
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
|
||||||
|
|
||||||
import java.util.Set;
|
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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface UserCredentialAuthenticationProvider {
|
public interface CredentialAuthentication {
|
||||||
Set<String> getSupportedCredentialAuthenticationTypes();
|
boolean supportsCredentialAuthenticationFor(String type);
|
||||||
CredentialValidationOutput validCredential(KeycloakSession session, RealmModel realm, UserCredentialModel input);
|
CredentialValidationOutput authenticate(RealmModel realm, CredentialInput input);
|
||||||
}
|
}
|
|
@ -49,6 +49,11 @@ public class CredentialValidationOutput {
|
||||||
return authStatus;
|
return authStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* State that is passed back by provider
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public Map<String, String> getState() {
|
public Map<String, String> getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,4 +90,17 @@ public interface UserCredentialManager extends UserCredentialStore {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
boolean isConfiguredLocally(RealmModel realm, UserModel user, String type);
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ public class UserFederationManager implements UserProvider {
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
|
public UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
|
||||||
return KeycloakModelUtils.getFederationProviderInstance(session, model);
|
return KeycloakModelUtils.getFederationProviderInstance(session, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,40 +484,6 @@ public class UserFederationManager implements UserProvider {
|
||||||
session.userStorage().preRemove(protocolMapper);
|
session.userStorage().preRemove(protocolMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
|
|
||||||
List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
|
|
||||||
List<UserFederationProvider> fedProviders = new ArrayList<UserFederationProvider>();
|
|
||||||
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
|
@Override
|
||||||
public void preRemove(RealmModel realm, ComponentModel component) {
|
public void preRemove(RealmModel realm, ComponentModel component) {
|
||||||
session.userStorage().preRemove(realm, component);
|
session.userStorage().preRemove(realm, component);
|
||||||
|
|
|
@ -75,10 +75,6 @@ public interface UserProvider extends Provider,
|
||||||
void preRemove(RealmModel realm, ClientModel client);
|
void preRemove(RealmModel realm, ClientModel client);
|
||||||
void preRemove(ProtocolMapperModel protocolMapper);
|
void preRemove(ProtocolMapperModel protocolMapper);
|
||||||
|
|
||||||
|
|
||||||
CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input);
|
|
||||||
|
|
||||||
|
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
void preRemove(RealmModel realm, ComponentModel component);
|
void preRemove(RealmModel realm, ComponentModel component);
|
||||||
|
|
|
@ -86,7 +86,7 @@ public class SpnegoAuthenticator extends AbstractUsernameFormAuthenticator imple
|
||||||
String spnegoToken = tokens[1];
|
String spnegoToken = tokens[1];
|
||||||
UserCredentialModel spnegoCredential = UserCredentialModel.kerberos(spnegoToken);
|
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) {
|
if (output.getAuthStatus() == CredentialValidationOutput.Status.AUTHENTICATED) {
|
||||||
context.setUser(output.getAuthenticatedUser());
|
context.setUser(output.getAuthenticatedUser());
|
||||||
|
|
|
@ -17,18 +17,23 @@
|
||||||
package org.keycloak.credential;
|
package org.keycloak.credential;
|
||||||
|
|
||||||
import org.keycloak.common.util.reflections.Types;
|
import org.keycloak.common.util.reflections.Types;
|
||||||
|
import org.keycloak.models.CredentialValidationOutput;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialManager;
|
import org.keycloak.models.UserCredentialManager;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserFederationProvider;
|
import org.keycloak.models.UserFederationProvider;
|
||||||
|
import org.keycloak.models.UserFederationProviderModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.cache.CachedUserModel;
|
import org.keycloak.models.cache.CachedUserModel;
|
||||||
import org.keycloak.models.cache.OnUserCache;
|
import org.keycloak.models.cache.OnUserCache;
|
||||||
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
import org.keycloak.provider.ProviderFactory;
|
||||||
import org.keycloak.storage.StorageId;
|
import org.keycloak.storage.StorageId;
|
||||||
import org.keycloak.storage.UserStorageManager;
|
import org.keycloak.storage.UserStorageManager;
|
||||||
import org.keycloak.storage.UserStorageProvider;
|
import org.keycloak.storage.UserStorageProvider;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -246,6 +251,37 @@ public class UserCredentialStoreManager implements UserCredentialManager, OnUser
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CredentialValidationOutput authenticate(KeycloakSession session, RealmModel realm, CredentialInput input) {
|
||||||
|
List<UserFederationProviderModel> fedProviderModels = realm.getUserFederationProviders();
|
||||||
|
List<UserFederationProvider> fedProviders = new ArrayList<UserFederationProvider>();
|
||||||
|
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<CredentialAuthentication> 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
|
@Override
|
||||||
public void onCache(RealmModel realm, CachedUserModel user, UserModel delegate) {
|
public void onCache(RealmModel realm, CachedUserModel user, UserModel delegate) {
|
||||||
List<OnUserCache> credentialProviders = getCredentialProviders(realm, OnUserCache.class);
|
List<OnUserCache> credentialProviders = getCredentialProviders(realm, OnUserCache.class);
|
||||||
|
|
|
@ -37,7 +37,7 @@ import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.cache.CachedUserModel;
|
import org.keycloak.models.cache.CachedUserModel;
|
||||||
import org.keycloak.models.cache.OnUserCache;
|
import org.keycloak.models.cache.OnUserCache;
|
||||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
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.UserLookupProvider;
|
||||||
import org.keycloak.storage.user.UserQueryProvider;
|
import org.keycloak.storage.user.UserQueryProvider;
|
||||||
import org.keycloak.storage.user.UserRegistrationProvider;
|
import org.keycloak.storage.user.UserRegistrationProvider;
|
||||||
|
@ -528,37 +528,6 @@ public class UserStorageManager implements UserProvider, OnUserCache {
|
||||||
if (getFederatedStorage() != null) getFederatedStorage().preRemove(protocolMapper);
|
if (getFederatedStorage() != null) getFederatedStorage().preRemove(protocolMapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public CredentialValidationOutput validCredentials(KeycloakSession session, RealmModel realm, UserCredentialModel... input) {
|
|
||||||
List<UserCredentialAuthenticationProvider> 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
|
@Override
|
||||||
public void preRemove(RealmModel realm, ComponentModel component) {
|
public void preRemove(RealmModel realm, ComponentModel component) {
|
||||||
if (!component.getProviderType().equals(UserStorageProvider.class.getName())) return;
|
if (!component.getProviderType().equals(UserStorageProvider.class.getName())) return;
|
||||||
|
|
|
@ -22,20 +22,17 @@ import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.authentication.AuthenticationFlow;
|
|
||||||
import org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory;
|
import org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory;
|
||||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.representations.idm.AuthenticationExecutionExportRepresentation;
|
|
||||||
import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
|
import org.keycloak.representations.idm.AuthenticationExecutionRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
import org.keycloak.representations.idm.AuthenticationFlowRepresentation;
|
||||||
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
||||||
import org.keycloak.representations.idm.RealmRepresentation;
|
import org.keycloak.representations.idm.RealmRepresentation;
|
||||||
import org.keycloak.representations.idm.UserRepresentation;
|
import org.keycloak.representations.idm.UserRepresentation;
|
||||||
import org.keycloak.storage.user.UserCredentialAuthenticationProvider;
|
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
import org.keycloak.testsuite.util.ExecutionBuilder;
|
import org.keycloak.testsuite.util.ExecutionBuilder;
|
||||||
|
@ -45,8 +42,6 @@ import org.keycloak.testsuite.util.UserBuilder;
|
||||||
|
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticator}
|
* Tests for {@link org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticator}
|
||||||
|
|
Loading…
Reference in a new issue