Add Support JdbcUserCredentialRepository
Closes gh-16224
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.aot.hint;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.predicate.RuntimeHintsPredicates;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link UserCredentialRuntimeHints}
|
||||
*
|
||||
* @author Max Batischev
|
||||
*/
|
||||
public class UserCredentialRuntimeHintsTests {
|
||||
|
||||
private final RuntimeHints hints = new RuntimeHints();
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
SpringFactoriesLoader.forResourceLocation("META-INF/spring/aot.factories")
|
||||
.load(RuntimeHintsRegistrar.class)
|
||||
.forEach((registrar) -> registrar.registerHints(this.hints, ClassUtils.getDefaultClassLoader()));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getClientRecordsSqlFiles")
|
||||
void credentialRecordsSqlFilesHasHints(String schemaFile) {
|
||||
assertThat(RuntimeHintsPredicates.resource().forResource(schemaFile)).accepts(this.hints);
|
||||
}
|
||||
|
||||
private static Stream<String> getClientRecordsSqlFiles() {
|
||||
return Stream.of("org/springframework/security/user-credentials-schema.sql");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package org.springframework.security.web.webauthn.api;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.Set;
|
||||
|
||||
public final class TestCredentialRecord {
|
||||
|
||||
public static ImmutableCredentialRecord.ImmutableCredentialRecordBuilder userCredential() {
|
||||
@@ -29,6 +32,24 @@ public final class TestCredentialRecord {
|
||||
.backupState(true);
|
||||
}
|
||||
|
||||
public static ImmutableCredentialRecord.ImmutableCredentialRecordBuilder fullUserCredential() {
|
||||
return ImmutableCredentialRecord.builder()
|
||||
.label("label")
|
||||
.credentialId(Bytes.fromBase64("NauGCN7bZ5jEBwThcde51g"))
|
||||
.userEntityUserId(Bytes.fromBase64("vKBFhsWT3gQnn-gHdT4VXIvjDkVXVYg5w8CLGHPunMM"))
|
||||
.publicKey(ImmutablePublicKeyCose.fromBase64(
|
||||
"pQECAyYgASFYIC7DAiV_trHFPjieOxXbec7q2taBcgLnIi19zrUwVhCdIlggvN6riHORK_velHcTLFK_uJhyKK0oBkJqzNqR2E-2xf8="))
|
||||
.backupEligible(true)
|
||||
.created(Instant.now())
|
||||
.transports(Set.of(AuthenticatorTransport.BLE, AuthenticatorTransport.HYBRID))
|
||||
.signatureCount(100)
|
||||
.uvInitialized(false)
|
||||
.credentialType(PublicKeyCredentialType.PUBLIC_KEY)
|
||||
.attestationObject(new Bytes("test".getBytes()))
|
||||
.attestationClientDataJSON(new Bytes(("test").getBytes()))
|
||||
.backupState(true);
|
||||
}
|
||||
|
||||
private TestCredentialRecord() {
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.security.web.webauthn.management;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.jdbc.core.JdbcOperations;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
|
||||
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.PublicKeyCredentialType;
|
||||
import org.springframework.security.web.webauthn.api.TestCredentialRecord;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link JdbcUserCredentialRepository}
|
||||
*
|
||||
* @author Max Batischev
|
||||
*/
|
||||
public class JdbcUserCredentialRepositoryTests {
|
||||
|
||||
private EmbeddedDatabase db;
|
||||
|
||||
private JdbcUserCredentialRepository jdbcUserCredentialRepository;
|
||||
|
||||
private static final String USER_CREDENTIALS_SQL_RESOURCE = "org/springframework/security/user-credentials-schema.sql";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
this.db = createDb();
|
||||
JdbcOperations jdbcOperations = new JdbcTemplate(this.db);
|
||||
this.jdbcUserCredentialRepository = new JdbcUserCredentialRepository(jdbcOperations);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
this.db.shutdown();
|
||||
}
|
||||
|
||||
private static EmbeddedDatabase createDb() {
|
||||
// @formatter:off
|
||||
return new EmbeddedDatabaseBuilder()
|
||||
.generateUniqueName(true)
|
||||
.setType(EmbeddedDatabaseType.HSQL)
|
||||
.setScriptEncoding("UTF-8")
|
||||
.addScript(USER_CREDENTIALS_SQL_RESOURCE)
|
||||
.build();
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWhenJdbcOperationsIsNullThenThrowIllegalArgumentException() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new JdbcUserCredentialRepository(null))
|
||||
.withMessage("jdbcOperations cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveWhenCredentialRecordIsNullThenThrowIllegalArgumentException() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.jdbcUserCredentialRepository.save(null))
|
||||
.withMessage("record cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByCredentialIdWheCredentialIdIsNullThenThrowIllegalArgumentException() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.jdbcUserCredentialRepository.findByCredentialId(null))
|
||||
.withMessage("credentialId cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void findByCredentialIdWheUserIdIsNullThenThrowIllegalArgumentException() {
|
||||
// @formatter:off
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> this.jdbcUserCredentialRepository.findByUserId(null))
|
||||
.withMessage("userId cannot be null");
|
||||
// @formatter:on
|
||||
}
|
||||
|
||||
@Test
|
||||
void saveCredentialRecordWhenSaveThenReturnsSaved() {
|
||||
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();
|
||||
this.jdbcUserCredentialRepository.save(userCredential);
|
||||
|
||||
CredentialRecord savedUserCredential = this.jdbcUserCredentialRepository
|
||||
.findByCredentialId(userCredential.getCredentialId());
|
||||
|
||||
assertThat(savedUserCredential).isNotNull();
|
||||
assertThat(savedUserCredential.getCredentialId()).isEqualTo(userCredential.getCredentialId());
|
||||
assertThat(savedUserCredential.getUserEntityUserId()).isEqualTo(userCredential.getUserEntityUserId());
|
||||
assertThat(savedUserCredential.getLabel()).isEqualTo(userCredential.getLabel());
|
||||
assertThat(savedUserCredential.getPublicKey().getBytes()).isEqualTo(userCredential.getPublicKey().getBytes());
|
||||
assertThat(savedUserCredential.isBackupEligible()).isEqualTo(userCredential.isBackupEligible());
|
||||
assertThat(savedUserCredential.isBackupState()).isEqualTo(userCredential.isBackupState());
|
||||
assertThat(savedUserCredential.getCreated()).isNotNull();
|
||||
assertThat(savedUserCredential.getLastUsed()).isNotNull();
|
||||
assertThat(savedUserCredential.isUvInitialized()).isFalse();
|
||||
assertThat(savedUserCredential.getSignatureCount()).isEqualTo(100);
|
||||
assertThat(savedUserCredential.getCredentialType()).isEqualTo(PublicKeyCredentialType.PUBLIC_KEY);
|
||||
assertThat(savedUserCredential.getTransports().contains(AuthenticatorTransport.HYBRID)).isTrue();
|
||||
assertThat(savedUserCredential.getTransports().contains(AuthenticatorTransport.BLE)).isTrue();
|
||||
assertThat(new String(savedUserCredential.getAttestationObject().getBytes())).isEqualTo("test");
|
||||
assertThat(new String(savedUserCredential.getAttestationClientDataJSON().getBytes())).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void findCredentialRecordByUserIdWhenRecordExistsThenReturnsSaved() {
|
||||
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();
|
||||
this.jdbcUserCredentialRepository.save(userCredential);
|
||||
|
||||
List<CredentialRecord> credentialRecords = this.jdbcUserCredentialRepository
|
||||
.findByUserId(userCredential.getUserEntityUserId());
|
||||
|
||||
assertThat(credentialRecords).isNotNull();
|
||||
assertThat(credentialRecords.size()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findCredentialRecordByUserIdWhenRecordDoesNotExistThenReturnsEmpty() {
|
||||
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();
|
||||
|
||||
List<CredentialRecord> credentialRecords = this.jdbcUserCredentialRepository
|
||||
.findByUserId(userCredential.getUserEntityUserId());
|
||||
|
||||
assertThat(credentialRecords.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
void findCredentialRecordByCredentialIdWhenRecordDoesNotExistThenReturnsNull() {
|
||||
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();
|
||||
|
||||
CredentialRecord credentialRecord = this.jdbcUserCredentialRepository
|
||||
.findByCredentialId(userCredential.getCredentialId());
|
||||
|
||||
assertThat(credentialRecord).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteCredentialRecordWhenRecordExistThenSuccess() {
|
||||
CredentialRecord userCredential = TestCredentialRecord.fullUserCredential().build();
|
||||
this.jdbcUserCredentialRepository.save(userCredential);
|
||||
|
||||
this.jdbcUserCredentialRepository.delete(userCredential.getCredentialId());
|
||||
|
||||
CredentialRecord credentialRecord = this.jdbcUserCredentialRepository
|
||||
.findByCredentialId(userCredential.getCredentialId());
|
||||
assertThat(credentialRecord).isNull();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user