Fix duplicate label when using password history (#42903)

Closes #42736

Signed-off-by: Alexander Schwartz <alexander.schwartz@gmx.net>
This commit is contained in:
Alexander Schwartz
2025-09-25 06:23:20 +02:00
committed by GitHub
parent ac121b4c50
commit a8d947b1a9
4 changed files with 11 additions and 6 deletions

View File

@@ -66,7 +66,7 @@ public class JpaUserCredentialStore implements UserCredentialStore {
if (!Objects.equals(cred.getUserLabel(), entity.getUserLabel())) {
// For legacy entries in the credentials, there might be a duplicate for historical reasons.
// Ignore them when the credential is updated, which might happen when credentials are verified.
validateDuplicateCredential(realm, user, cred.getUserLabel(), cred.getId());
validateDuplicateCredential(realm, user, cred.getType(), cred.getUserLabel(), cred.getId());
}
entity.setCreatedDate(cred.getCreatedDate());
entity.setUserLabel(cred.getUserLabel());
@@ -135,7 +135,7 @@ public class JpaUserCredentialStore implements UserCredentialStore {
@Override
public CredentialModel getStoredCredentialByNameAndType(RealmModel realm, UserModel user, String name, String type) {
return getStoredCredentialsStream(realm, user).filter(credential ->
Objects.equals(type, credential.getType()) && Objects.equals(name, credential.getUserLabel()))
Objects.equals(type, credential.getType()) && Objects.equals(name, credential.getUserLabel()))
.findFirst().orElse(null);
}
@@ -144,12 +144,13 @@ public class JpaUserCredentialStore implements UserCredentialStore {
}
private void validateDuplicateCredential(RealmModel realm, UserModel user, String userLabel, String credentialId) {
private void validateDuplicateCredential(RealmModel realm, UserModel user, String credType, String userLabel, String credentialId) {
if (userLabel != null) {
boolean exists = getStoredCredentialEntities(realm, user)
.anyMatch(existing -> existing.getUserLabel() != null
&& existing.getUserLabel().equalsIgnoreCase(userLabel.trim())
&& (credentialId == null || !existing.getId().equals(credentialId))); // Exclude self in update
&& existing.getType().equals(credType)
&& !existing.getId().equals(credentialId)); // Exclude self in update
if (exists) {
throw new ModelDuplicateException("Device already exists with the same name", CredentialModel.USER_LABEL);
@@ -158,7 +159,7 @@ public class JpaUserCredentialStore implements UserCredentialStore {
}
CredentialEntity createCredentialEntity(RealmModel realm, UserModel user, CredentialModel cred) {
validateDuplicateCredential(realm, user, cred.getUserLabel(), null);
validateDuplicateCredential(realm, user, cred.getType(), cred.getUserLabel(), null);
CredentialEntity entity = new CredentialEntity();
String id = cred.getId() == null ? KeycloakModelUtils.generateId() : cred.getId();
entity.setId(id);

View File

@@ -127,6 +127,8 @@ public class PasswordCredentialProvider implements CredentialProvider<PasswordCr
if (expiredPasswordsPolicyValue > 1 || passwordAgeInDaysPolicy > 0) {
oldPassword.setId(null);
oldPassword.setType(PasswordCredentialModel.PASSWORD_HISTORY);
// Setting the label to "nulL" avoids duplicate label errors
oldPassword.setUserLabel(null);
oldPassword = user.credentialManager().createStoredCredential(oldPassword);
}
}

View File

@@ -76,6 +76,8 @@ public class PasswordHistoryPolicyTest extends AbstractAuthTest {
newCredential.setValue(newPassword);
newCredential.setTemporary(false);
userResource.resetPassword(newCredential);
CredentialRepresentation cr = userResource.credentials().stream().filter(credentialRepresentation -> credentialRepresentation.getType().equals(PASSWORD)).findFirst().get();
userResource.setCredentialUserLabel(cr.getId(), "My Password");
}
private void expectBadRequestException(Consumer<Void> f) {

View File

@@ -95,7 +95,7 @@ public class WebAuthnSigningInTest extends AbstractWebAuthnAccountTest {
public void createWebAuthnSameUserLabel() {
final String SAME_LABEL = "key123";
SigningInPage.UserCredential webAuthn = addWebAuthnCredential(SAME_LABEL, false);
SigningInPage.UserCredential webAuthn = addWebAuthnCredential(SAME_LABEL, true);
assertThat(webAuthn, notNullValue());
SigningInPage.CredentialType credentialType = webAuthnPwdlessCredentialType;