Skip to content

Commit

Permalink
Fix JdbcUserCredentialRepository Save
Browse files Browse the repository at this point in the history
Closes gh-16620

Signed-off-by: Max Batischev <[email protected]>
  • Loading branch information
franticticktick committed Feb 19, 2025
1 parent 51ce91f commit 2f41603
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@
import java.util.Set;
import java.util.function.Function;

import org.springframework.dao.DuplicateKeyException;
import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementSetter;
Expand Down Expand Up @@ -112,6 +113,24 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit

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.
* @param jdbcOperations the JDBC operations
Expand All @@ -133,6 +152,21 @@ public void delete(Bytes credentialId) {
@Override
public void save(CredentialRecord record) {
Assert.notNull(record, "record cannot be null");
boolean existsRecord = null != this.findByCredentialId(record.getCredentialId());
if (existsRecord) {
updateCredentialRecord(record);
}
else {
try {
insertCredentialRecord(record);
}
catch (DuplicateKeyException ex) {
updateCredentialRecord(record);
}
}
}

private void insertCredentialRecord(CredentialRecord record) {
List<SqlParameterValue> parameters = this.credentialRecordParametersMapper.apply(record);
try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator,
Expand All @@ -141,6 +175,17 @@ public void save(CredentialRecord record) {
}
}

private void 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());
this.jdbcOperations.update(UPDATE_CREDENTIAL_RECORD_SQL, pss);
}
}

@Override
public CredentialRecord findByCredentialId(Bytes credentialId) {
Assert.notNull(credentialId, "credentialId cannot be null");
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -29,6 +29,7 @@
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.security.web.webauthn.api.AuthenticatorTransport;
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.TestCredentialRecord;

Expand Down Expand Up @@ -133,6 +134,27 @@ void saveCredentialRecordWhenSaveThenReturnsSaved() {
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
void findCredentialRecordByUserIdWhenRecordExistsThenReturnsSaved() {
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();
Expand Down

0 comments on commit 2f41603

Please sign in to comment.