io.helidon.integrations.oci.secrets.configsource.SecretBundleLazyConfigSource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of helidon-integrations-oci-secrets-config-source Show documentation
Show all versions of helidon-integrations-oci-secrets-config-source Show documentation
OCI Secrets Retrieval API ConfigSourceProvider Implementation
/*
* Copyright (c) 2023, 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.integrations.oci.secrets.configsource;
import java.lang.System.Logger;
import java.util.Base64;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import io.helidon.common.LazyValue;
import io.helidon.config.AbstractConfigSource;
import io.helidon.config.Config;
import io.helidon.config.ConfigException;
import io.helidon.config.spi.ConfigNode;
import io.helidon.config.spi.LazyConfigSource;
import com.oracle.bmc.model.BmcException;
import com.oracle.bmc.secrets.Secrets;
import com.oracle.bmc.secrets.model.Base64SecretBundleContentDetails;
import com.oracle.bmc.secrets.requests.GetSecretBundleByNameRequest;
import static java.lang.System.Logger.Level.DEBUG;
/**
* An {@link AbstractConfigSource} and a {@link LazyConfigSource} implementation that sources its values from the Oracle
* Cloud Infrastructure (OCI) Secrets
* Retrieval and Vault APIs.
*/
public final class SecretBundleLazyConfigSource
extends AbstractSecretBundleConfigSource
implements LazyConfigSource {
private static final Logger LOGGER = System.getLogger(SecretBundleLazyConfigSource.class.getName());
private final Function> nodeFunction;
private SecretBundleLazyConfigSource(Builder b) {
super(b);
String vaultOcid = b.vaultOcid();
if (vaultOcid == null) {
this.nodeFunction = secretName -> Optional.empty();
} else {
LazyValue extends Secrets> secretsSupplier = LazyValue.create(b.secretsSupplier()::get);
this.nodeFunction = secretName -> node(b.acceptPattern, secretsSupplier, vaultOcid, secretName);
}
}
/**
* Creates and returns a new {@link Builder} for {@linkplain Builder#build() building} {@link
* SecretBundleLazyConfigSource} instances.
*
* @return a new {@link Builder}
*/
public static Builder builder() {
return new Builder();
}
static Optional node(Supplier> secretBundleContentDetailsSupplier) {
Object secretBundleContentDetails = secretBundleContentDetailsSupplier.get();
if (secretBundleContentDetails instanceof Base64SecretBundleContentDetails base64SecretBundleContentDetails) {
return Optional.of(valueNode(base64SecretBundleContentDetails.getContent(), Base64.getDecoder()));
}
return Optional.empty();
}
static GetSecretBundleByNameRequest request(String vaultOcid, String secretName) {
return GetSecretBundleByNameRequest.builder()
.vaultId(vaultOcid)
.secretName(secretName)
.build();
}
@Deprecated // For use by the Helidon Config subsystem only.
@Override // NodeConfigSource
public Optional node(String key) {
return this.nodeFunction.apply(key);
}
private static Optional node(Pattern acceptPattern,
LazyValue extends Secrets> secretsSupplier,
String vaultOcid,
String secretName) {
if (!acceptPattern.matcher(secretName).matches()) {
if (LOGGER.isLoggable(DEBUG)) {
LOGGER.log(DEBUG, "Ignoring ConfigNode request for name "
+ secretName
+ " because it was not matched by "
+ acceptPattern);
}
return Optional.empty();
}
Secrets s = secretsSupplier.get();
return node(() -> secretBundleContentDetails(s, vaultOcid, secretName));
}
private static Object secretBundleContentDetails(Secrets s, String vaultOcid, String secretName) {
try {
if (LOGGER.isLoggable(DEBUG)) {
LOGGER.log(DEBUG, "Getting SecretBundle with name " + secretName);
}
return s.getSecretBundleByName(request(vaultOcid, secretName)).getSecretBundle().getSecretBundleContent();
} catch (BmcException e) {
if (e.getStatusCode() == 404) {
return null;
}
throw e;
}
}
/**
* An {@link AbstractSecretBundleConfigSource.Builder} that {@linkplain #build() builds} {@link
* SecretBundleLazyConfigSource} instances.
*/
public static final class Builder extends AbstractSecretBundleConfigSource.Builder {
private static final Pattern ACCEPT_EVERYTHING_PATTERN = Pattern.compile("^.*$");
private Pattern acceptPattern;
private Builder() {
super();
this.acceptPattern = ACCEPT_EVERYTHING_PATTERN;
}
/**
* Sets the {@link Pattern} that will dictate which configuration property names are allowed to reach a {@link
* SecretBundleLazyConfigSource} instance.
*
* @param acceptPattern the {@link Pattern}
* @return this {@link Builder}
* @throws NullPointerException if {@code acceptPattern} is {@code null}
*/
public Builder acceptPattern(Pattern acceptPattern) {
this.acceptPattern = Objects.requireNonNull(acceptPattern, "acceptPattern");
return this;
}
/**
* Creates and returns a new {@link SecretBundleLazyConfigSource} instance initialized from the state of this
* {@link Builder}.
*
* @return a new {@link SecretBundleLazyConfigSource}
*/
public SecretBundleLazyConfigSource build() {
return new SecretBundleLazyConfigSource(this);
}
/**
* Configures this {@link Builder} from the supplied meta-configuration.
*
* @param metaConfig the meta-configuration; must not be {@code null}
* @return this {@link Builder}
* @throws io.helidon.config.ConfigException if a {@code change-watcher} or {@code polling-strategy} is
* specified
* @throws NullPointerException if {@code metaConfig} is {@code null}
* @throws java.util.regex.PatternSyntaxException if the {@code accept-pattern} key's value could not be
* {@linkplain Pattern#compile(String) compiled}
*/
@Override // AbstractSecretBundleConfigSource.Builder
public Builder config(Config metaConfig) {
metaConfig.get("polling-strategy")
.asNode()
.ifPresent(n -> {
throw new ConfigException("Invalid meta-configuration key: polling-strategy: "
+ "Polling is not supported by "
+ this.getClass().getName() + " instances");
});
metaConfig.get("accept-pattern")
.asString()
.ifPresent(s -> this.acceptPattern(Pattern.compile(s)));
return super.config(metaConfig);
}
}
}