Skip to content

Commit

Permalink
3.x: Introduces Lazy OCI Vault ConfigSource (backport of #7470)
Browse files Browse the repository at this point in the history
Signed-off-by: Laird Nelson <laird.nelson@oracle.com>
  • Loading branch information
ljnelson committed Aug 28, 2023
1 parent 2366fbc commit 03c12af
Show file tree
Hide file tree
Showing 9 changed files with 472 additions and 102 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/3.1.0/spotbugs/etc/findbugsfilter.xsd">

<Match>
<Class name="io.helidon.integrations.oci.secrets.configsource.SecretBundleConfigSource"/>
<Class name="io.helidon.integrations.oci.secrets.configsource.SecretBundleNodeConfigSource"/>
<Method name="secretSummaries"/>
<!-- Spotbugs is very, very confused by try-with-resources blocks. -->
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"/>
Expand Down
1 change: 1 addition & 0 deletions integrations/oci/oci-secrets-config-source/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<properties>
<compartment-ocid/>
<java.util.logging.config.file>src/test/java/logging.properties</java.util.logging.config.file>
<lazy>false</lazy>
<spotbugs.exclude>etc/spotbugs/exclude.xml</spotbugs.exclude>
<vault-ocid/>
</properties>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* Copyright (c) 2023 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.integrations.oci.secrets.configsource;

import java.lang.System.Logger;
import java.util.Base64;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.function.Supplier;

import io.helidon.common.LazyValue;
import io.helidon.config.AbstractConfigSource;
import io.helidon.config.AbstractConfigSourceBuilder;
import io.helidon.config.Config;
import io.helidon.config.ConfigException;
import io.helidon.config.spi.ConfigNode.ValueNode;

import com.oracle.bmc.auth.BasicAuthenticationDetailsProvider;
import com.oracle.bmc.secrets.Secrets;
import com.oracle.bmc.secrets.SecretsClient;

import static io.helidon.integrations.oci.sdk.runtime.OciExtension.ociAuthenticationProvider;
import static java.lang.System.Logger.Level.WARNING;
import static java.nio.charset.StandardCharsets.UTF_8;

/**
* An {@link AbstractConfigSource} that encapsulates functionality common to both {@link SecretBundleLazyConfigSource}
* and {@link SecretBundleNodeConfigSource}.
*
* @param <B> the type of {@link AbstractConfigSourceBuilder} subclass used to build instances of this class
*
* @see SecretBundleLazyConfigSource
*
* @see SecretBundleNodeConfigSource
*/
public abstract sealed class AbstractSecretBundleConfigSource<B extends AbstractConfigSourceBuilder<B, Void>>
extends AbstractConfigSource
permits SecretBundleLazyConfigSource, SecretBundleNodeConfigSource {

private static final Logger LOGGER = System.getLogger(AbstractSecretBundleConfigSource.class.getName());

static final String VAULT_OCID_PROPERTY_NAME = "vault-ocid";

/**
* Creates a new {@link AbstractSecretBundleConfigSource}.
*
* @param b a builder
*/
protected AbstractSecretBundleConfigSource(B b) {
super(b);
}

static ValueNode valueNode(String base64EncodedContent, Base64.Decoder base64Decoder) {
String decodedContent = new String(base64Decoder.decode(base64EncodedContent), UTF_8);
return ValueNode.create(decodedContent.intern());
}

/**
* An {@link AbstractConfigSourceBuilder} used to build instances of {@link AbstractSecretBundleConfigSource}.
*
* @param <B> the builder subclass
*/
public abstract static sealed class Builder<B extends AbstractConfigSourceBuilder<B, Void>>
extends AbstractConfigSourceBuilder<B, Void>
permits SecretBundleLazyConfigSource.Builder, SecretBundleNodeConfigSource.Builder {

private Supplier<? extends Secrets> secretsSupplier;

private String vaultOcid;

/**
* Creates a new {@link Builder}.
*/
protected Builder() {
super();
SecretsClient.Builder scb = SecretsClient.builder();
this.secretsSupplier = () -> scb.build(adpSupplier().get());
}

/**
* Configures this {@link Builder} from the supplied meta-configuration.
*
* @param metaConfig the meta-configuration; must not be {@code null}
*
* @return this {@link Builder}
*
* @exception NullPointerException if {@code metaConfig} is {@code null}
*/
@Override // AbstractConfigSourceBuilder<Builder, Void>
public B config(Config metaConfig) {
metaConfig.get("change-watcher")
.asNode()
.ifPresent(n -> {
throw new ConfigException("Invalid meta-configuration key: change-watcher: "
+ "Change watching is not supported by "
+ this.getClass().getName() + " instances");
});
metaConfig.get("vault-ocid")
.asString()
.filter(Predicate.not(String::isBlank))
.ifPresentOrElse(this::vaultOcid,
() -> {
if (LOGGER.isLoggable(WARNING)) {
LOGGER.log(WARNING,
"No meta-configuration value supplied for "
+ metaConfig.key().toString() + "." + VAULT_OCID_PROPERTY_NAME
+ "); resulting ConfigSource will be empty");
}
});
return super.config(metaConfig);
}

/**
* Sets the (required) OCID of the OCI vault from which an {@link AbstractSecretBundleConfigSource} will
* retrieve values.
*
* @param vaultOcid a valid OCID identifying an OCI vault; must not be {@code null}
*
* @return this {@link Builder}
*
* @exception NullPointerException if {@code vaultId} is {@code null}
*/
@SuppressWarnings("unchecked")
public B vaultOcid(String vaultOcid) {
this.vaultOcid = Objects.requireNonNull(vaultOcid, "vaultOcid");
return (B) this;
}

String vaultOcid() {
return this.vaultOcid;
}

/**
* Uses the supplied {@link Supplier} of {@link Secrets} instances, instead of the default one, for
* communicating with the OCI Secrets Retrieval API.
*
* @param secretsSupplier the non-default {@link Supplier} to use; must not be {@code null}
*
* @return this {@link Builder}
*
* @exception NullPointerException if {@code secretsSupplier} is {@code null}
*/
@SuppressWarnings("unchecked")
public B secretsSupplier(Supplier<? extends Secrets> secretsSupplier) {
this.secretsSupplier = Objects.requireNonNull(secretsSupplier, "secretsSupplier");
return (B) this;
}

Supplier<? extends Secrets> secretsSupplier() {
return this.secretsSupplier;
}

static LazyValue<? extends BasicAuthenticationDetailsProvider> adpSupplier() {
return LazyValue.create(() -> (BasicAuthenticationDetailsProvider) ociAuthenticationProvider().get());
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import java.util.Set;

import io.helidon.config.AbstractConfigSource;
import io.helidon.config.Config;
import io.helidon.config.spi.ConfigSource;
import io.helidon.config.spi.ConfigSourceProvider;
Expand Down Expand Up @@ -93,30 +94,33 @@ public OciSecretsConfigSourceProvider() {


/**
* Creates and returns a non-{@code null} {@link SecretBundleConfigSource} that sources its values from an Oracle
* Cloud Infrastructure (OCI) <a
* Creates and returns a non-{@code null} {@link AbstractConfigSource} implementation that sources its values from
* an Oracle Cloud Infrastructure (OCI) <a
* href="https://docs.oracle.com/en-us/iaas/Content/KeyManagement/Concepts/keyoverview.htm">Vault</a>.
*
* @param type one of the {@linkplain #supported() supported types}; not actually used
*
* @param metaConfig a {@link Config} serving as meta-configuration for this provider; must not be {@code null} when
* {@code type} is {@linkplain #supports(String) supported}
*
* @return a non-{@code null} {@link SecretBundleConfigSource}
* @return a non-{@code null} {@link AbstractConfigSource} implementation
*
* @exception NullPointerException if {@code type} is {@linkplain #supports(String) supported} and {@code
* metaConfig} is {@code null}
*
* @see #supported()
*
* @see SecretBundleConfigSource
* @see AbstractConfigSource
*
* @deprecated For use by the Helidon Config subsystem only.
*/
@Deprecated // For use by the Helidon Config subsystem only.
@Override // ConfigSourceProvider
public SecretBundleConfigSource create(String type, Config metaConfig) {
return SecretBundleConfigSource.builder().config(metaConfig).build();
public AbstractConfigSource create(String type, Config metaConfig) {
if (metaConfig.get("lazy").asBoolean().orElse(Boolean.FALSE)) {
return SecretBundleLazyConfigSource.builder().config(metaConfig).build();
}
return SecretBundleNodeConfigSource.builder().config(metaConfig).build();
}

/**
Expand Down
Loading

0 comments on commit 03c12af

Please sign in to comment.