Pārlūkot izejas kodu

Fix JdbcUserCredentialRepository Save

Closes gh-16620

Signed-off-by: Max Batischev <mblancer@mail.ru>
Max Batischev 6 mēneši atpakaļ
vecāks
revīzija
47630ca354

+ 37 - 1
web/src/main/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepository.java

@@ -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.
@@ -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;
 
+	// @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
@@ -133,6 +151,13 @@ public final class JdbcUserCredentialRepository implements UserCredentialReposit
 	@Override
 	public void save(CredentialRecord record) {
 		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);
 		try (LobCreator lobCreator = this.lobHandler.getLobCreator()) {
 			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
 	public CredentialRecord findByCredentialId(Bytes credentialId) {
 		Assert.notNull(credentialId, "credentialId cannot be null");

+ 23 - 1
web/src/test/java/org/springframework/security/web/webauthn/management/JdbcUserCredentialRepositoryTests.java

@@ -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.
@@ -29,6 +29,7 @@ import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
 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;
 
@@ -133,6 +134,27 @@ public class JdbcUserCredentialRepositoryTests {
 		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();