Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config encryption improvements #8440

Merged
merged 2 commits into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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();

}
27 changes: 19 additions & 8 deletions config/config/src/main/java/io/helidon/config/ProviderImpl.java
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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<Config, ConfigFilter> configFilterProvider : filterProviders) {
stringValue = configFilterProvider.apply(config).apply(key, stringValue);
toReturn = configFilterProvider.apply(config).apply(key, toReturn);
}
return stringValue;
return toReturn;
}

void enableCaching() {
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;

Expand Down Expand Up @@ -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<String> processedValues = new HashSet<>();

Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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;

/**
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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<List<String>> passwordsOpt = getConfig().get("passwords")
Expand Down