diff --git a/spring-vault-core/src/main/java/org/springframework/vault/core/util/KeyValueDelegate.java b/spring-vault-core/src/main/java/org/springframework/vault/core/util/KeyValueDelegate.java index 0cb053eb..a05552f7 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/core/util/KeyValueDelegate.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/core/util/KeyValueDelegate.java @@ -23,6 +23,7 @@ import java.util.function.Supplier; import org.springframework.lang.Nullable; import org.springframework.util.ConcurrentReferenceHashMap; import org.springframework.util.StringUtils; +import org.springframework.vault.VaultException; import org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend; import org.springframework.vault.core.VaultOperations; import org.springframework.vault.support.VaultResponse; @@ -127,6 +128,9 @@ public class KeyValueDelegate { mountInfo = doGetMountInfo(path); } + catch (VaultException e) { + return MountInfo.unavailable(); + } catch (RuntimeException e) { mountInfo = MountInfo.unavailable(); } diff --git a/spring-vault-core/src/main/java/org/springframework/vault/repository/core/VaultKeyValueAdapter.java b/spring-vault-core/src/main/java/org/springframework/vault/repository/core/VaultKeyValueAdapter.java index edbcae54..e9e6adde 100644 --- a/spring-vault-core/src/main/java/org/springframework/vault/repository/core/VaultKeyValueAdapter.java +++ b/spring-vault-core/src/main/java/org/springframework/vault/repository/core/VaultKeyValueAdapter.java @@ -255,6 +255,10 @@ public class VaultKeyValueAdapter extends AbstractKeyValueAdapter { KeyValueDelegate.MountInfo mountInfo = keyValueDelegate.getMountInfo(keyspace); + if (!mountInfo.isAvailable()) { + throw new VaultException("Cannot determine MountInfo"); + } + return accessors.computeIfAbsent(keyspace, it -> { if (keyValueDelegate.isVersioned(it)) { diff --git a/spring-vault-core/src/test/java/org/springframework/vault/repository/core/VaultKeyValueAdapterIntegrationTests.java b/spring-vault-core/src/test/java/org/springframework/vault/repository/core/VaultKeyValueAdapterIntegrationTests.java new file mode 100644 index 00000000..49e89266 --- /dev/null +++ b/spring-vault-core/src/test/java/org/springframework/vault/repository/core/VaultKeyValueAdapterIntegrationTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 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. + * 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.vault.repository.core; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.vault.VaultException; +import org.springframework.vault.core.VaultIntegrationTestConfiguration; +import org.springframework.vault.core.VaultTemplate; +import org.springframework.vault.core.util.KeyValueDelegate; +import org.springframework.vault.domain.Person; +import org.springframework.vault.util.IntegrationTestSupport; + +import static org.assertj.core.api.Assertions.*; + +/** + * Integration tests for {@link VaultKeyValueAdapter}. + * + * @author Mark Paluch + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = VaultIntegrationTestConfiguration.class) +class VaultKeyValueAdapterIntegrationTests extends IntegrationTestSupport { + + @Autowired + VaultTemplate template; + + @Test + void shouldFailOnAbsentKeyspace() { + + VaultKeyValueAdapter adapter = new VaultKeyValueAdapter(template); + + assertThatExceptionOfType(VaultException.class).isThrownBy(() -> adapter.get("some-id", "absent", Person.class)) + .withMessageContaining("Cannot determine MountInfo"); + } + + @Test + void shouldReturnVersionedMountInfo() { + + KeyValueDelegate delegate = new KeyValueDelegate(template); + + KeyValueDelegate.MountInfo mountInfo = delegate.getMountInfo("versioned/nothing/here"); + + assertThat(mountInfo.isAvailable()).isTrue(); + assertThat(mountInfo.getOptions()).containsEntry("version", "2"); + } + + @Test + void shouldReturnUnversionedMountInfo() { + + KeyValueDelegate delegate = new KeyValueDelegate(template); + + KeyValueDelegate.MountInfo mountInfo = delegate.getMountInfo("secret/nothing/here"); + + assertThat(mountInfo.isAvailable()).isTrue(); + assertThat(mountInfo.getOptions()).doesNotContainEntry("version", "2"); + } + +} diff --git a/src/main/antora/modules/ROOT/pages/vault/vault-repositories.adoc b/src/main/antora/modules/ROOT/pages/vault/vault-repositories.adoc index 24353a2c..66c23c11 100644 --- a/src/main/antora/modules/ROOT/pages/vault/vault-repositories.adoc +++ b/src/main/antora/modules/ROOT/pages/vault/vault-repositories.adoc @@ -9,6 +9,8 @@ As of version 2.4, Spring Vault can use additionally key/value version 2 secrets NOTE: Deletes within versioned key/value secrets engine use the `DELETE` operation. Secrets are not destroyed through `CrudRepository.delete(…)`. +NOTE: Vault Repositories determine the mount path through Vault's `sys/internal/ui/mounts/…` endpoint. Make sure that your policy allows accessing that path, otherwise you won't be able to use the repository abstraction. + NOTE: Read more about Spring Data Repositories in the https://docs.spring.io/spring-data/commons/docs/current/reference/html/#repositories[Spring Data Commons reference documentation]. The reference documentation will give you an introduction to Spring Data repositories.