From 529612facc67b2dd383b4a93570018966534217e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Kr=C3=A1l?= Date: Thu, 7 Mar 2024 10:27:22 +0100 Subject: [PATCH] Improving ConfigFilter behavior (#8440) Improving ConfigFilter behavior Signed-off-by: David Kral --- .../helidon/config/ConfigItemBlueprint.java | 43 +++++++++++++++++++ .../java/io/helidon/config/ProviderImpl.java | 27 ++++++++---- .../io/helidon/config/spi/ConfigFilter.java | 25 ++++++++++- .../config/encryption/EncryptionFilter.java | 16 ++++++- .../encryption/EncryptionFilterService.java | 11 ++++- .../encryption/AbstractSecureConfigTest.java | 15 ++++++- 6 files changed, 125 insertions(+), 12 deletions(-) create mode 100644 config/config/src/main/java/io/helidon/config/ConfigItemBlueprint.java diff --git a/config/config/src/main/java/io/helidon/config/ConfigItemBlueprint.java b/config/config/src/main/java/io/helidon/config/ConfigItemBlueprint.java new file mode 100644 index 00000000000..69be588b902 --- /dev/null +++ b/config/config/src/main/java/io/helidon/config/ConfigItemBlueprint.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. + * + * 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 + * + * http://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 io.helidon.config; + +import io.helidon.builder.api.Prototype; + +/** + * Configuration item policy. + * Contains information about the given item to improve its handling. + */ +@Prototype.Blueprint +interface ConfigItemBlueprint { + + /** + * Whether to cache this handled config item or not. + * If overall caching is disabled, this will not turn it on even if set to true. + * + * @return whether to cache handled config item + */ + boolean cacheItem(); + + /** + * Handled config item. + * + * @return config item + */ + String item(); + +} diff --git a/config/config/src/main/java/io/helidon/config/ProviderImpl.java b/config/config/src/main/java/io/helidon/config/ProviderImpl.java index 03cae301729..046c1f053c7 100644 --- a/config/config/src/main/java/io/helidon/config/ProviderImpl.java +++ b/config/config/src/main/java/io/helidon/config/ProviderImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023 Oracle and/or its affiliates. + * Copyright (c) 2017, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -312,22 +312,33 @@ void addFilter(ConfigFilter filter) { public String apply(Config.Key key, String stringValue) { if (cachingEnabled) { if (!valueCache.containsKey(key)) { - String value = proceedFilters(key, stringValue); - valueCache.put(key, value); + ConfigItem configItem = ConfigItem.builder() + .cacheItem(cachingEnabled) + .item(stringValue) + .build(); + configItem = proceedFilters(key, configItem); + String value = configItem.item(); + if (configItem.cacheItem()) { + valueCache.put(key, value); + } return value; } return valueCache.get(key); } else { - return proceedFilters(key, stringValue); + ConfigItem configItem = ConfigItem.builder() + .cacheItem(cachingEnabled) + .item(stringValue) + .build(); + return proceedFilters(key, configItem).item(); } - } - private String proceedFilters(Config.Key key, String stringValue) { + private ConfigItem proceedFilters(Config.Key key, ConfigItem configItem) { + ConfigItem toReturn = configItem; for (Function configFilterProvider : filterProviders) { - stringValue = configFilterProvider.apply(config).apply(key, stringValue); + toReturn = configFilterProvider.apply(config).apply(key, toReturn); } - return stringValue; + return toReturn; } void enableCaching() { diff --git a/config/config/src/main/java/io/helidon/config/spi/ConfigFilter.java b/config/config/src/main/java/io/helidon/config/spi/ConfigFilter.java index e673b2b319b..f6994eea604 100644 --- a/config/config/src/main/java/io/helidon/config/spi/ConfigFilter.java +++ b/config/config/src/main/java/io/helidon/config/spi/ConfigFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2023 Oracle and/or its affiliates. + * Copyright (c) 2017, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package io.helidon.config.spi; import io.helidon.config.Config; +import io.helidon.config.ConfigItem; /** * Filter that can transform elementary configuration ({@code String}) values @@ -65,6 +66,28 @@ public interface ConfigFilter { */ String apply(Config.Key key, String stringValue); + /** + * Filters an elementary config value before it is made available to the + * application via the {@code Config} API. Returns {@link ConfigItem} object + * which contains filtered config value and specific value settings. + * + * @param key configuration {@link Config#key() key} associated with the + * {@code Config} node + * @param itemPolicy original item policy + * @return new item policy object with the filtered config value + */ + default ConfigItem apply(Config.Key key, ConfigItem itemPolicy) { + String originalItem = itemPolicy.item(); + String newItem = apply(key, originalItem); + if (newItem.equals(originalItem)) { + return itemPolicy; + } + return ConfigItem.builder() + .from(itemPolicy) + .item(newItem) + .build(); + } + /** * Initializes the filter using the {@code Config} instance which the filter * will affect once {@code Config.Builder.build} completes. diff --git a/config/encryption/src/main/java/io/helidon/config/encryption/EncryptionFilter.java b/config/encryption/src/main/java/io/helidon/config/encryption/EncryptionFilter.java index decfbcca678..71aa48d3839 100644 --- a/config/encryption/src/main/java/io/helidon/config/encryption/EncryptionFilter.java +++ b/config/encryption/src/main/java/io/helidon/config/encryption/EncryptionFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2023 Oracle and/or its affiliates. + * Copyright (c) 2018, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,6 +26,7 @@ import io.helidon.common.pki.Keys; import io.helidon.config.Config; +import io.helidon.config.ConfigItem; import io.helidon.config.MissingValueException; import io.helidon.config.spi.ConfigFilter; @@ -132,6 +133,19 @@ public String apply(Config.Key key, String stringValue) { return maybeDecode(key, stringValue); } + @Override + public ConfigItem apply(Config.Key key, ConfigItem itemPolicy) { + String item = apply(key, itemPolicy.item()); + if (item.equals(itemPolicy.item())) { + //This was not config item handled by this filter. + return itemPolicy; + } + return ConfigItem.builder() + .cacheItem(false) + .item(item) + .build(); + } + private String maybeDecode(Config.Key key, String value) { Set processedValues = new HashSet<>(); diff --git a/config/encryption/src/main/java/io/helidon/config/encryption/EncryptionFilterService.java b/config/encryption/src/main/java/io/helidon/config/encryption/EncryptionFilterService.java index 29ed9cdb548..d19e57a7f21 100644 --- a/config/encryption/src/main/java/io/helidon/config/encryption/EncryptionFilterService.java +++ b/config/encryption/src/main/java/io/helidon/config/encryption/EncryptionFilterService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package io.helidon.config.encryption; import io.helidon.config.Config; +import io.helidon.config.ConfigItem; import io.helidon.config.spi.ConfigFilter; /** @@ -34,6 +35,14 @@ public String apply(Config.Key key, String stringValue) { return filter.apply(key, stringValue); } + @Override + public ConfigItem apply(Config.Key key, ConfigItem itemPolicy) { + if (null == filter) { + return itemPolicy; + } + return filter.apply(key, itemPolicy); + } + @Override public void init(Config config) { this.filter = EncryptionFilter.fromConfig().apply(config); diff --git a/config/encryption/src/test/java/io/helidon/config/encryption/AbstractSecureConfigTest.java b/config/encryption/src/test/java/io/helidon/config/encryption/AbstractSecureConfigTest.java index 9eaafd890a7..6e1b8398baa 100644 --- a/config/encryption/src/test/java/io/helidon/config/encryption/AbstractSecureConfigTest.java +++ b/config/encryption/src/test/java/io/helidon/config/encryption/AbstractSecureConfigTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2021 Oracle and/or its affiliates. + * Copyright (c) 2018, 2024 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,9 +27,12 @@ import static io.helidon.config.ConfigValues.simpleValue; import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.hasSize; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; /** @@ -87,6 +90,16 @@ void testSymmetric() { testPassword(getConfig(), "pwd6", ""); } + @Test + void testDecryptedValuesNotCached() { + String value1 = getConfig().get("pwd4").asString().get(); + String value2 = getConfig().get("pwd4").asString().get(); + assertThat(value1, is(TEST_STRING)); + assertThat(value2, is(TEST_STRING)); + //This is intended, since we want to verify, that the new String has been created and was not reused + assertThat(value1, not(sameInstance(value2))); + } + @Test public void testPasswordArray() { ConfigValue> passwordsOpt = getConfig().get("passwords")