move credential validation to UserProvider
This commit is contained in:
parent
a5593469ae
commit
f342a8c7a3
18 changed files with 211 additions and 131 deletions
|
@ -52,7 +52,7 @@ public abstract class AbstractModelAuthenticationProvider implements Authenticat
|
||||||
RealmModel realm = getRealm(currentRealm, config);
|
RealmModel realm = getRealm(currentRealm, config);
|
||||||
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(keycloakSession, realm, username);
|
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(keycloakSession, realm, username);
|
||||||
|
|
||||||
boolean result = realm.validatePassword(user, password);
|
boolean result = keycloakSession.users().validCredentials(realm, user, UserCredentialModel.password(password));
|
||||||
return result ? AuthProviderStatus.SUCCESS : AuthProviderStatus.INVALID_CREDENTIALS;
|
return result ? AuthProviderStatus.SUCCESS : AuthProviderStatus.INVALID_CREDENTIALS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
package org.keycloak.models;
|
|
||||||
|
|
||||||
import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
|
||||||
* @version $Revision: 1 $
|
|
||||||
*/
|
|
||||||
public class CredentialValidation {
|
|
||||||
|
|
||||||
private static int hashIterations(RealmModel realm) {
|
|
||||||
PasswordPolicy policy = realm.getPasswordPolicy();
|
|
||||||
if (policy != null) {
|
|
||||||
return policy.getHashIterations();
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Will update password if hash iteration policy has changed
|
|
||||||
*
|
|
||||||
* @param realm
|
|
||||||
* @param user
|
|
||||||
* @param password
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static boolean validatePassword(RealmModel realm, UserModel user, String password) {
|
|
||||||
boolean validated = false;
|
|
||||||
UserCredentialValueModel passwordCred = null;
|
|
||||||
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
|
|
||||||
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
|
||||||
validated = new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue(), cred.getHashIterations());
|
|
||||||
passwordCred = cred;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (validated) {
|
|
||||||
int iterations = hashIterations(realm);
|
|
||||||
if (iterations > -1 && iterations != passwordCred.getHashIterations()) {
|
|
||||||
UserCredentialValueModel newCred = new UserCredentialValueModel();
|
|
||||||
newCred.setType(passwordCred.getType());
|
|
||||||
newCred.setDevice(passwordCred.getDevice());
|
|
||||||
newCred.setSalt(passwordCred.getSalt());
|
|
||||||
newCred.setHashIterations(iterations);
|
|
||||||
newCred.setValue(new Pbkdf2PasswordEncoder(newCred.getSalt()).encode(password, iterations));
|
|
||||||
user.updateCredentialDirectly(newCred);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return validated;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -106,10 +106,6 @@ public interface RealmModel extends RoleContainerModel {
|
||||||
|
|
||||||
void setPasswordPolicy(PasswordPolicy policy);
|
void setPasswordPolicy(PasswordPolicy policy);
|
||||||
|
|
||||||
boolean validatePassword(UserModel user, String password);
|
|
||||||
|
|
||||||
boolean validateTOTP(UserModel user, String password, String token);
|
|
||||||
|
|
||||||
RoleModel getRoleById(String id);
|
RoleModel getRoleById(String id);
|
||||||
|
|
||||||
List<String> getDefaultRoles();
|
List<String> getDefaultRoles();
|
||||||
|
|
|
@ -35,6 +35,13 @@ public class UserCredentialModel {
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static UserCredentialModel totp(String key) {
|
||||||
|
UserCredentialModel model = new UserCredentialModel();
|
||||||
|
model.setType(TOTP);
|
||||||
|
model.setValue(key);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
public static UserCredentialModel generateSecret() {
|
public static UserCredentialModel generateSecret() {
|
||||||
UserCredentialModel model = new UserCredentialModel();
|
UserCredentialModel model = new UserCredentialModel();
|
||||||
model.setType(SECRET);
|
model.setType(SECRET);
|
||||||
|
|
|
@ -33,6 +33,7 @@ public interface UserProvider extends Provider {
|
||||||
void preRemove(RealmModel realm);
|
void preRemove(RealmModel realm);
|
||||||
void preRemove(RoleModel role);
|
void preRemove(RoleModel role);
|
||||||
|
|
||||||
|
boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
|
||||||
|
boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
|
||||||
void close();
|
void close();
|
||||||
}
|
}
|
||||||
|
|
130
model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
Executable file
130
model/api/src/main/java/org/keycloak/models/utils/CredentialValidation.java
Executable file
|
@ -0,0 +1,130 @@
|
||||||
|
package org.keycloak.models.utils;
|
||||||
|
|
||||||
|
import org.keycloak.models.PasswordPolicy;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class CredentialValidation {
|
||||||
|
|
||||||
|
private static int hashIterations(RealmModel realm) {
|
||||||
|
PasswordPolicy policy = realm.getPasswordPolicy();
|
||||||
|
if (policy != null) {
|
||||||
|
return policy.getHashIterations();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will update password if hash iteration policy has changed
|
||||||
|
*
|
||||||
|
* @param realm
|
||||||
|
* @param user
|
||||||
|
* @param password
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean validPassword(RealmModel realm, UserModel user, String password) {
|
||||||
|
boolean validated = false;
|
||||||
|
UserCredentialValueModel passwordCred = null;
|
||||||
|
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
|
||||||
|
if (cred.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||||
|
validated = new Pbkdf2PasswordEncoder(cred.getSalt()).verify(password, cred.getValue(), cred.getHashIterations());
|
||||||
|
passwordCred = cred;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validated) {
|
||||||
|
int iterations = hashIterations(realm);
|
||||||
|
if (iterations > -1 && iterations != passwordCred.getHashIterations()) {
|
||||||
|
UserCredentialValueModel newCred = new UserCredentialValueModel();
|
||||||
|
newCred.setType(passwordCred.getType());
|
||||||
|
newCred.setDevice(passwordCred.getDevice());
|
||||||
|
newCred.setSalt(passwordCred.getSalt());
|
||||||
|
newCred.setHashIterations(iterations);
|
||||||
|
newCred.setValue(new Pbkdf2PasswordEncoder(newCred.getSalt()).encode(password, iterations));
|
||||||
|
user.updateCredentialDirectly(newCred);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return validated;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean validTOTP(RealmModel realm, UserModel user, String otp) {
|
||||||
|
UserCredentialValueModel passwordCred = null;
|
||||||
|
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
|
||||||
|
if (cred.getType().equals(UserCredentialModel.TOTP)) {
|
||||||
|
if (new TimeBasedOTP().validate(otp, cred.getValue().getBytes())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
public static boolean validSecret(RealmModel realm, UserModel user, String secret) {
|
||||||
|
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
|
||||||
|
if (cred.getType().equals(UserCredentialModel.SECRET)) {
|
||||||
|
if (cred.getValue().equals(secret)) return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must validate all credentials. FYI, password hashes may be rehashed and updated based on realm hash password policies.
|
||||||
|
*
|
||||||
|
* @param realm
|
||||||
|
* @param user
|
||||||
|
* @param credentials
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> credentials) {
|
||||||
|
for (UserCredentialModel credential : credentials) {
|
||||||
|
if (!validCredential(realm, user, credential)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must validate all credentials. FYI, password hashes may be rehashed and updated based on realm hash password policies.
|
||||||
|
*
|
||||||
|
* @param realm
|
||||||
|
* @param user
|
||||||
|
* @param credentials
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... credentials) {
|
||||||
|
for (UserCredentialModel credential : credentials) {
|
||||||
|
if (!validCredential(realm, user, credential)) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean validCredential(RealmModel realm, UserModel user, UserCredentialModel credential) {
|
||||||
|
if (credential.getType().equals(UserCredentialModel.PASSWORD)) {
|
||||||
|
if (!validPassword(realm, user, credential.getValue())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (credential.getType().equals(UserCredentialModel.TOTP)) {
|
||||||
|
if (!validTOTP(realm, user, credential.getValue())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (credential.getType().equals(UserCredentialModel.SECRET)) {
|
||||||
|
if (!validSecret(realm, user, credential.getValue())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import org.keycloak.models.KeycloakTransaction;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.SocialLinkModel;
|
import org.keycloak.models.SocialLinkModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.cache.entities.CachedUser;
|
import org.keycloak.models.cache.entities.CachedUser;
|
||||||
|
@ -231,6 +232,16 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
|
||||||
return getDelegate().removeSocialLink(realm, user, socialProvider);
|
return getDelegate().removeSocialLink(realm, user, socialProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
|
||||||
|
return getDelegate().validCredentials(realm, user, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
|
||||||
|
return getDelegate().validCredentials(realm, user, input);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preRemove(RealmModel realm) {
|
public void preRemove(RealmModel realm) {
|
||||||
realmInvalidations.add(realm.getId());
|
realmInvalidations.add(realm.getId());
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
package org.keycloak.models.cache;
|
package org.keycloak.models.cache;
|
||||||
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakTransaction;
|
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.SocialLinkModel;
|
import org.keycloak.models.SocialLinkModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.cache.entities.CachedUser;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -114,6 +111,16 @@ public class NoCacheUserProvider implements CacheUserProvider {
|
||||||
return getDelegate().removeSocialLink(realm, user, socialProvider);
|
return getDelegate().removeSocialLink(realm, user, socialProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
|
||||||
|
return getDelegate().validCredentials(realm, user, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
|
||||||
|
return getDelegate().validCredentials(realm, user, input);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preRemove(RealmModel realm) {
|
public void preRemove(RealmModel realm) {
|
||||||
getDelegate().preRemove(realm);
|
getDelegate().preRemove(realm);
|
||||||
|
|
|
@ -3,7 +3,7 @@ package org.keycloak.models.cache;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.AuthenticationProviderModel;
|
import org.keycloak.models.AuthenticationProviderModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.CredentialValidation;
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
import org.keycloak.models.OAuthClientModel;
|
import org.keycloak.models.OAuthClientModel;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
import org.keycloak.models.PasswordPolicy;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -375,22 +375,6 @@ public class RealmAdapter implements RealmModel {
|
||||||
updated.setPasswordPolicy(policy);
|
updated.setPasswordPolicy(policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validatePassword(UserModel user, String password) {
|
|
||||||
return CredentialValidation.validatePassword(this, user, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validateTOTP(UserModel user, String password, String token) {
|
|
||||||
if (!validatePassword(user, password)) return false;
|
|
||||||
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
|
|
||||||
if (cred.getType().equals(UserCredentialModel.TOTP)) {
|
|
||||||
return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RoleModel getRoleById(String id) {
|
public RoleModel getRoleById(String id) {
|
||||||
if (updated != null) return updated.getRoleById(id);
|
if (updated != null) return updated.getRoleById(id);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.SocialLinkModel;
|
import org.keycloak.models.SocialLinkModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.jpa.entities.RealmEntity;
|
import org.keycloak.models.jpa.entities.RealmEntity;
|
||||||
|
@ -12,6 +13,7 @@ import org.keycloak.models.jpa.entities.RoleEntity;
|
||||||
import org.keycloak.models.jpa.entities.SocialLinkEntity;
|
import org.keycloak.models.jpa.entities.SocialLinkEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserEntity;
|
import org.keycloak.models.jpa.entities.UserEntity;
|
||||||
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
|
import org.keycloak.models.jpa.entities.UserRoleMappingEntity;
|
||||||
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
@ -274,4 +276,13 @@ public class JpaUserProvider implements UserProvider {
|
||||||
return (entity != null) ? new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()) : null;
|
return (entity != null) ? new SocialLinkModel(entity.getSocialProvider(), entity.getSocialUserId(), entity.getSocialUsername()) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
|
||||||
|
return CredentialValidation.validCredentials(realm, user, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
|
||||||
|
return CredentialValidation.validCredentials(realm, user, input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package org.keycloak.models.jpa;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.AuthenticationProviderModel;
|
import org.keycloak.models.AuthenticationProviderModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.CredentialValidation;
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.OAuthClientModel;
|
import org.keycloak.models.OAuthClientModel;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
import org.keycloak.models.PasswordPolicy;
|
||||||
|
@ -795,22 +795,6 @@ public class RealmAdapter implements RealmModel {
|
||||||
return role.getContainer().removeRole(role);
|
return role.getContainer().removeRole(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validatePassword(UserModel user, String password) {
|
|
||||||
return CredentialValidation.validatePassword(this, user, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validateTOTP(UserModel user, String password, String token) {
|
|
||||||
if (!validatePassword(user, password)) return false;
|
|
||||||
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
|
|
||||||
if (cred.getType().equals(UserCredentialModel.TOTP)) {
|
|
||||||
return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PasswordPolicy getPasswordPolicy() {
|
public PasswordPolicy getPasswordPolicy() {
|
||||||
if (passwordPolicy == null) {
|
if (passwordPolicy == null) {
|
||||||
|
|
|
@ -9,10 +9,12 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.SocialLinkModel;
|
import org.keycloak.models.SocialLinkModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserProvider;
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.entities.SocialLinkEntity;
|
import org.keycloak.models.entities.SocialLinkEntity;
|
||||||
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
|
||||||
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -311,4 +313,14 @@ public class MongoUserProvider implements UserProvider {
|
||||||
public void preRemove(RoleModel role) {
|
public void preRemove(RoleModel role) {
|
||||||
// todo not sure what to do for this
|
// todo not sure what to do for this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input) {
|
||||||
|
return CredentialValidation.validCredentials(realm, user, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) {
|
||||||
|
return CredentialValidation.validCredentials(realm, user, input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
import org.keycloak.models.ApplicationModel;
|
import org.keycloak.models.ApplicationModel;
|
||||||
import org.keycloak.models.AuthenticationProviderModel;
|
import org.keycloak.models.AuthenticationProviderModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.CredentialValidation;
|
import org.keycloak.models.utils.CredentialValidation;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmProvider;
|
import org.keycloak.models.RealmProvider;
|
||||||
import org.keycloak.models.OAuthClientModel;
|
import org.keycloak.models.OAuthClientModel;
|
||||||
|
@ -723,22 +723,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validatePassword(UserModel user, String password) {
|
|
||||||
return CredentialValidation.validatePassword(this, user, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean validateTOTP(UserModel user, String password, String token) {
|
|
||||||
if (!validatePassword(user, password)) return false;
|
|
||||||
for (UserCredentialValueModel cred : user.getCredentialsDirectly()) {
|
|
||||||
if (cred.getType().equals(UserCredentialModel.TOTP)) {
|
|
||||||
return new TimeBasedOTP().validate(token, cred.getValue().getBytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void updateRealm() {
|
protected void updateRealm() {
|
||||||
super.updateMongoEntity();
|
super.updateMongoEntity();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ import org.keycloak.models.SocialLinkModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserProvider;
|
||||||
import org.keycloak.models.utils.RepresentationToModel;
|
import org.keycloak.models.utils.RepresentationToModel;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
|
@ -129,16 +130,17 @@ public class AdapterTest extends AbstractModelTest {
|
||||||
@Test
|
@Test
|
||||||
public void testCredentialValidation() throws Exception {
|
public void testCredentialValidation() throws Exception {
|
||||||
test1CreateRealm();
|
test1CreateRealm();
|
||||||
UserModel user = realmManager.getSession().users().addUser(realmModel, "bburke");
|
UserProvider userProvider = realmManager.getSession().users();
|
||||||
|
UserModel user = userProvider.addUser(realmModel, "bburke");
|
||||||
UserCredentialModel cred = new UserCredentialModel();
|
UserCredentialModel cred = new UserCredentialModel();
|
||||||
cred.setType(CredentialRepresentation.PASSWORD);
|
cred.setType(CredentialRepresentation.PASSWORD);
|
||||||
cred.setValue("geheim");
|
cred.setValue("geheim");
|
||||||
user.updateCredential(cred);
|
user.updateCredential(cred);
|
||||||
Assert.assertTrue(realmModel.validatePassword(user, "geheim"));
|
Assert.assertTrue(userProvider.validCredentials(realmModel, user, UserCredentialModel.password("geheim")));
|
||||||
List<UserCredentialValueModel> creds = user.getCredentialsDirectly();
|
List<UserCredentialValueModel> creds = user.getCredentialsDirectly();
|
||||||
Assert.assertEquals(creds.get(0).getHashIterations(), 1);
|
Assert.assertEquals(creds.get(0).getHashIterations(), 1);
|
||||||
realmModel.setPasswordPolicy( new PasswordPolicy("hashIterations(200)"));
|
realmModel.setPasswordPolicy( new PasswordPolicy("hashIterations(200)"));
|
||||||
Assert.assertTrue(realmModel.validatePassword(user, "geheim"));
|
Assert.assertTrue(userProvider.validCredentials(realmModel, user, UserCredentialModel.password("geheim")));
|
||||||
creds = user.getCredentialsDirectly();
|
creds = user.getCredentialsDirectly();
|
||||||
Assert.assertEquals(creds.get(0).getHashIterations(), 200);
|
Assert.assertEquals(creds.get(0).getHashIterations(), 200);
|
||||||
realmModel.setPasswordPolicy( new PasswordPolicy("hashIterations(1)"));
|
realmModel.setPasswordPolicy( new PasswordPolicy("hashIterations(1)"));
|
||||||
|
|
|
@ -158,7 +158,7 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
|
||||||
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(session, null, realm, formData));
|
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(session, null, realm, formData));
|
||||||
|
|
||||||
// Password updated just in LDAP, so validating directly in realm should fail
|
// Password updated just in LDAP, so validating directly in realm should fail
|
||||||
Assert.assertFalse(realm.validatePassword(john, "password-updated"));
|
Assert.assertFalse(session.users().validCredentials(realm, john, UserCredentialModel.password("password-updated")));
|
||||||
|
|
||||||
// Switch to not allow updating passwords in ldap
|
// Switch to not allow updating passwords in ldap
|
||||||
AuthProvidersExternalModelTest.setPasswordUpdateForProvider(false, AuthProviderConstants.PROVIDER_NAME_PICKETLINK, realm);
|
AuthProvidersExternalModelTest.setPasswordUpdateForProvider(false, AuthProviderConstants.PROVIDER_NAME_PICKETLINK, realm);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
@ -31,8 +32,9 @@ public class MultipleRealmsTest extends AbstractModelTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUsers() {
|
public void testUsers() {
|
||||||
UserModel r1user1 = realmManager.getSession().users().getUserByUsername("user1", realm1);
|
UserProvider userProvider = realmManager.getSession().users();
|
||||||
UserModel r2user1 = realmManager.getSession().users().getUserByUsername("user1", realm2);
|
UserModel r1user1 = userProvider.getUserByUsername("user1", realm1);
|
||||||
|
UserModel r2user1 = userProvider.getUserByUsername("user1", realm2);
|
||||||
Assert.assertEquals(r1user1.getUsername(), r2user1.getUsername());
|
Assert.assertEquals(r1user1.getUsername(), r2user1.getUsername());
|
||||||
Assert.assertNotEquals(r1user1.getId(), r2user1.getId());
|
Assert.assertNotEquals(r1user1.getId(), r2user1.getId());
|
||||||
|
|
||||||
|
@ -40,22 +42,22 @@ public class MultipleRealmsTest extends AbstractModelTest {
|
||||||
r1user1.updateCredential(UserCredentialModel.password("pass1"));
|
r1user1.updateCredential(UserCredentialModel.password("pass1"));
|
||||||
r2user1.updateCredential(UserCredentialModel.password("pass2"));
|
r2user1.updateCredential(UserCredentialModel.password("pass2"));
|
||||||
|
|
||||||
Assert.assertTrue(realm1.validatePassword(r1user1, "pass1"));
|
Assert.assertTrue(userProvider.validCredentials(realm1, r1user1, UserCredentialModel.password("pass1")));
|
||||||
Assert.assertFalse(realm1.validatePassword(r1user1, "pass2"));
|
Assert.assertFalse(userProvider.validCredentials(realm1, r1user1, UserCredentialModel.password("pass2")));
|
||||||
Assert.assertFalse(realm2.validatePassword(r2user1, "pass1"));
|
Assert.assertFalse(userProvider.validCredentials(realm2, r2user1, UserCredentialModel.password("pass1")));
|
||||||
Assert.assertTrue(realm2.validatePassword(r2user1, "pass2"));
|
Assert.assertTrue(userProvider.validCredentials(realm2, r2user1, UserCredentialModel.password("pass2")));
|
||||||
|
|
||||||
// Test searching
|
// Test searching
|
||||||
Assert.assertEquals(2, realmManager.getSession().users().searchForUser("user", realm1).size());
|
Assert.assertEquals(2, userProvider.searchForUser("user", realm1).size());
|
||||||
|
|
||||||
commit();
|
commit();
|
||||||
realm1 = model.getRealm("id1");
|
realm1 = model.getRealm("id1");
|
||||||
realm2 = model.getRealm("id2");
|
realm2 = model.getRealm("id2");
|
||||||
|
|
||||||
realmManager.getSession().users().removeUser(realm1, "user1");
|
userProvider.removeUser(realm1, "user1");
|
||||||
realmManager.getSession().users().removeUser(realm1, "user2");
|
userProvider.removeUser(realm1, "user2");
|
||||||
Assert.assertEquals(0, realmManager.getSession().users().searchForUser("user", realm1).size());
|
Assert.assertEquals(0, userProvider.searchForUser("user", realm1).size());
|
||||||
Assert.assertEquals(2, realmManager.getSession().users().searchForUser("user", realm2).size());
|
Assert.assertEquals(2, userProvider.searchForUser("user", realm2).size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.keycloak.models.AuthenticationLinkModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredCredentialModel;
|
import org.keycloak.models.RequiredCredentialModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
@ -296,7 +297,7 @@ public class AuthenticationManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.debug("validating TOTP");
|
logger.debug("validating TOTP");
|
||||||
if (!realm.validateTOTP(user, password, token)) {
|
if (!session.users().validCredentials(realm, user, UserCredentialModel.totp(token))) {
|
||||||
return AuthenticationStatus.INVALID_CREDENTIALS;
|
return AuthenticationStatus.INVALID_CREDENTIALS;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import org.apache.log.Logger;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.SocialLinkModel;
|
import org.keycloak.models.SocialLinkModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
@ -101,7 +102,7 @@ public class ReadUsersWorker implements Worker {
|
||||||
|
|
||||||
// Validate password (shoould be same as username)
|
// Validate password (shoould be same as username)
|
||||||
if (readPassword) {
|
if (readPassword) {
|
||||||
realm.validatePassword(user, username);
|
session.users().validCredentials(realm, user, UserCredentialModel.password(username));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read socialLinks of user
|
// Read socialLinks of user
|
||||||
|
|
Loading…
Reference in a new issue