Skip to content

Commit e28b9bd

Browse files
Fix JdbcUserCredentialRepository Save
Closes gh-16620 Signed-off-by: Max Batischev <[email protected]>
1 parent 51ce91f commit e28b9bd

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

web/src/main/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepository.java

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -112,6 +112,24 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
112112

113113
private static final String DELETE_CREDENTIAL_RECORD_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + ID_FILTER;
114114

115+
// @formatter:off
116+
private static final String UPDATE_CREDENTIAL_RECORD_SQL = "UPDATE " + TABLE_NAME
117+
+ " SET user_entity_user_id = ?, " +
118+
"public_key = ?, " +
119+
"signature_count = ?, " +
120+
"uv_initialized = ?, " +
121+
"backup_eligible = ? ," +
122+
"authenticator_transports = ?, " +
123+
"public_key_credential_type = ?, " +
124+
"backup_state = ?, " +
125+
"attestation_object = ?, " +
126+
"attestation_client_data_json = ?, " +
127+
"created = ?, " +
128+
"last_used = ?, " +
129+
"label = ?"
130+
+ " WHERE " + ID_FILTER;
131+
// @formatter:on
132+
115133
/**
116134
* Constructs a {@code JdbcUserCredentialRepository} using the provided parameters.
117135
* @param jdbcOperations the JDBC operations
@@ -133,6 +151,13 @@ public void delete(Bytes credentialId) {
133151
@Override
134152
public void save(CredentialRecord record) {
135153
Assert.notNull(record, "record cannot be null");
154+
int rows = updateCredentialRecord(record);
155+
if (rows == 0) {
156+
insertCredentialRecord(record);
157+
}
158+
}
159+
160+
private void insertCredentialRecord(CredentialRecord record) {
136161
List<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);
137162
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
138163
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,
@@ -141,6 +166,17 @@ public void save(CredentialRecord record) {
141166
}
142167
}
143168

169+
private int updateCredentialRecord(CredentialRecord record) {
170+
List<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);
171+
SqlParameterValue credentialId = parameters.remove(0);
172+
parameters.add(credentialId);
173+
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
174+
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,
175+
parameters.toArray());
176+
return this.jdbcOperations.update(UPDATE_CREDENTIAL_RECORD_SQL, pss);
177+
}
178+
}
179+
144180
@Override
145181
public CredentialRecord findByCredentialId(Bytes credentialId) {
146182
Assert.notNull(credentialId, "credentialId cannot be null");

web/src/test/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepositoryTests.java

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2024 the original author or authors.
2+
* Copyright 2002-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
3030
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
3131
import org.springframework.security.web.webauthn.api.CredentialRecord;
32+
import org.springframework.security.web.webauthn.api.ImmutableCredentialRecord;
3233
import org.springframework.security.web.webauthn.api.PublicKeyCredentialType;
3334
import org.springframework.security.web.webauthn.api.TestCredentialRecord;
3435

@@ -133,6 +134,27 @@ void saveCredentialRecordWhenSaveThenReturnsSaved() {
133134
assertThat(new String(savedUserCredential.getAttestationClientDataJSON().getBytes())).isEqualTo("test");
134135
}
135136

137+
@Test
138+
void saveCredentialRecordWhenRecordExistsThenReturnsUpdated() {
139+
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();
140+
this.jdbcUserCredentialRepository.save(userCredential);
141+
// @formatter:off
142+
CredentialRecord updatedRecord = ImmutableCredentialRecord.fromCredentialRecord(userCredential)
143+
.backupEligible(false)
144+
.uvInitialized(true)
145+
.signatureCount(200).build();
146+
// @formatter:on
147+
148+
this.jdbcUserCredentialRepository.save(updatedRecord);
149+
150+
CredentialRecord record = this.jdbcUserCredentialRepository
151+
.findByCredentialId(userCredential.getCredentialId());
152+
153+
assertThat(record.getSignatureCount()).isEqualTo(200);
154+
assertThat(record.isUvInitialized()).isTrue();
155+
assertThat(record.isBackupEligible()).isFalse();
156+
}
157+
136158
@Test
137159
void findCredentialRecordByUserIdWhenRecordExistsThenReturnsSaved() {
138160
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();

0 commit comments

Comments
 (0)