Fix JdbcUserCredentialRepository Save

Closes gh-16620

Signed-off-by: Max Batischev <mblancer@mail.ru>
This commit is contained in:
Max Batischev 2025-02-19 14:43:44 +03:00 committed by Josh Cummings
parent 58a665e5aa
commit 47630ca354
2 changed files with 60 additions and 2 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -112,6 +112,24 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
private static final String DELETE_CREDENTIAL_RECORD_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + ID_FILTER; private static final String DELETE_CREDENTIAL_RECORD_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + ID_FILTER;
// @formatter:off
private static final String UPDATE_CREDENTIAL_RECORD_SQL = "UPDATE " + TABLE_NAME
+ " SET user_entity_user_id = ?, " +
"public_key = ?, " +
"signature_count = ?, " +
"uv_initialized = ?, " +
"backup_eligible = ? ," +
"authenticator_transports = ?, " +
"public_key_credential_type = ?, " +
"backup_state = ?, " +
"attestation_object = ?, " +
"attestation_client_data_json = ?, " +
"created = ?, " +
"last_used = ?, " +
"label = ?"
+ " WHERE " + ID_FILTER;
// @formatter:on
/** /**
* Constructs a {@code JdbcUserCredentialRepository} using the provided parameters. * Constructs a {@code JdbcUserCredentialRepository} using the provided parameters.
* @param jdbcOperations the JDBC operations * @param jdbcOperations the JDBC operations
@ -133,6 +151,13 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
@Override @Override
public void save(CredentialRecord record) { public void save(CredentialRecord record) {
Assert.notNull(record, "record cannot be null"); Assert.notNull(record, "record cannot be null");
int rows = updateCredentialRecord(record);
if (rows == 0) {
insertCredentialRecord(record);
}
}
private void insertCredentialRecord(CredentialRecord record) {
List<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record); List<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) { try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator, PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,
@ -141,6 +166,17 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
} }
} }
private int updateCredentialRecord(CredentialRecord record) {
List<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);
SqlParameterValue credentialId = parameters.remove(0);
parameters.add(credentialId);
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,
parameters.toArray());
return this.jdbcOperations.update(UPDATE_CREDENTIAL_RECORD_SQL, pss);
}
}
@Override @Override
public CredentialRecord findByCredentialId(Bytes credentialId) { public CredentialRecord findByCredentialId(Bytes credentialId) {
Assert.notNull(credentialId, "credentialId cannot be null"); Assert.notNull(credentialId, "credentialId cannot be null");

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -29,6 +29,7 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.web.webauthn.api.AuthenticatorTransport; import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
import org.springframework.security.web.webauthn.api.CredentialRecord; import org.springframework.security.web.webauthn.api.CredentialRecord;
import org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType; import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
import org.springframework.security.web.webauthn.api.TestCredentialRecord; import org.springframework.security.web.webauthn.api.TestCredentialRecord;
@ -133,6 +134,27 @@ public class JdbcUserCredentialRepositoryTests {
assertThat(new String(savedUserCredential.getAttestationClientDataJSON().getBytes())).isEqualTo("test"); assertThat(new String(savedUserCredential.getAttestationClientDataJSON().getBytes())).isEqualTo("test");
} }
@Test
void saveCredentialRecordWhenRecordExistsThenReturnsUpdated() {
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();
this.jdbcUserCredentialRepository.save(userCredential);
// @formatter:off
CredentialRecord updatedRecord = ImmutableCredentialRecord.fromCredentialRecord(userCredential)
.backupEligible(false)
.uvInitialized(true)
.signatureCount(200).build();
// @formatter:on
this.jdbcUserCredentialRepository.save(updatedRecord);
CredentialRecord record = this.jdbcUserCredentialRepository
.findByCredentialId(userCredential.getCredentialId());
assertThat(record.getSignatureCount()).isEqualTo(200);
assertThat(record.isUvInitialized()).isTrue();
assertThat(record.isBackupEligible()).isFalse();
}
@Test @Test
void findCredentialRecordByUserIdWhenRecordExistsThenReturnsSaved() { void findCredentialRecordByUserIdWhenRecordExistsThenReturnsSaved() {
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build(); CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();