Skip to content

Commit

Permalink
KeyStore ConfigSource
Browse files Browse the repository at this point in the history
  • Loading branch information
radcortez committed Mar 21, 2023
1 parent f8f8008 commit 350cc09
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,13 @@ private boolean validExtension(final Path fileName) {
}

private boolean validExtension(final String resourceName) {
for (String s : getFileExtensions()) {
String[] fileExtensions = getFileExtensions();

if (fileExtensions.length == 0) {
return true;
}

for (String s : fileExtensions) {
if (resourceName.endsWith(s)) {
return true;
}
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
<module>sources/file-system</module>
<module>sources/yaml</module>
<module>sources/zookeeper</module>
<module>sources/keystore</module>
<module>converters/json</module>
<module>utils/events</module>
<module>utils/cdi-provider</module>
Expand Down
37 changes: 37 additions & 0 deletions sources/keystore/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>smallrye-config-parent</artifactId>
<groupId>io.smallrye.config</groupId>
<version>2.12.2-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

<artifactId>smallrye-config-source-keystore</artifactId>

<name>SmallRye: MicroProfile Config Source - KeyStore</name>

<dependencies>
<dependency>
<groupId>io.smallrye.config</groupId>
<artifactId>smallrye-config</artifactId>
</dependency>

<!-- Test -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>
<dependency>
<groupId>io.smallrye.testing</groupId>
<artifactId>smallrye-testing-utilities</artifactId>
</dependency>
<dependency>
<groupId>jakarta.annotation</groupId>
<artifactId>jakarta.annotation-api</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.smallrye.config.source.keystore;

import io.smallrye.config.ConfigMapping;
import io.smallrye.config.WithDefault;
import io.smallrye.config.WithParentName;

import java.util.Map;
import java.util.Optional;

@ConfigMapping(prefix = "io.smallrye.config.source.keystore")
public interface KeyStoreConfig {
@WithParentName
Map<String, KeyStore> keystores();

interface KeyStore {
String path();

@WithDefault("PKCS12")
String type();

String password();

Map<String, Alias> aliases();

interface Alias {
Optional<String> name();

Optional<String> password();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package io.smallrye.config.source.keystore;

import io.smallrye.config.AbstractLocationConfigSourceFactory;
import io.smallrye.config.ConfigSourceContext;
import io.smallrye.config.ConfigSourceFactory;
import io.smallrye.config.ConfigValue;
import io.smallrye.config.ConfigurableConfigSource;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;
import io.smallrye.config.source.keystore.KeyStoreConfig.KeyStore.Alias;
import org.eclipse.microprofile.config.spi.ConfigSource;

import java.io.IOException;
import java.net.URL;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

import static java.nio.charset.StandardCharsets.UTF_8;

public class KeyStoreConfigSourceFactory implements ConfigSourceFactory {
@Override
public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) {
SmallRyeConfig config = new SmallRyeConfigBuilder()
.withSources(new ContextConfigSource(context))
.withMapping(KeyStoreConfig.class)
.build();

KeyStoreConfig keyStoreConfig = config.getConfigMapping(KeyStoreConfig.class);

List<ConfigSource> keyStoreSources = new ArrayList<>();
for (Map.Entry<String, KeyStoreConfig.KeyStore> keyStoreEntry : keyStoreConfig.keystores().entrySet()) {
KeyStoreConfig.KeyStore keyStore = keyStoreEntry.getValue();

keyStoreSources.add(new ConfigurableConfigSource(new AbstractLocationConfigSourceFactory() {
@Override
protected String[] getFileExtensions() {
return new String[0];
}

@Override
protected ConfigSource loadConfigSource(final URL url, final int ordinal) {
return new UrlKeyStoreConfigSource(url, ordinal).loadKeyStore(keyStore);
}

@Override
public Iterable<ConfigSource> getConfigSources(final ConfigSourceContext context) {
return loadConfigSources(keyStore.path(), 100);
}
}));
}

return keyStoreSources;
}

private static class ContextConfigSource implements ConfigSource {
private final ConfigSourceContext context;

public ContextConfigSource(final ConfigSourceContext context) {
this.context = context;
}

@Override
public Set<String> getPropertyNames() {
Set<String> names = new HashSet<>();
Iterator<String> namesIterator = context.iterateNames();
while (namesIterator.hasNext()) {
names.add(namesIterator.next());
}
return names;
}

@Override
public String getValue(final String propertyName) {
ConfigValue value = context.getValue(propertyName);
return value != null && value.getValue() != null ? value.getValue() : null;
}

@Override
public String getName() {
return ContextConfigSource.class.getName();
}
}

private static class UrlKeyStoreConfigSource implements ConfigSource {
private final URL url;
private final int ordinal;

UrlKeyStoreConfigSource(final URL url, final int ordinal) {
this.url = url;
this.ordinal = ordinal;
}

ConfigSource loadKeyStore(KeyStoreConfig.KeyStore keyStoreConfig) {
try {
KeyStore keyStore = KeyStore.getInstance(keyStoreConfig.type());
keyStore.load(url.openStream(), keyStoreConfig.password().toCharArray());

Map<String, String> properties = new HashMap<>();
Enumeration<String> aliases = keyStore.aliases();
while (aliases.hasMoreElements()) {
String alias = aliases.nextElement();
Alias aliasConfig = keyStoreConfig.aliases().getOrDefault(alias, new Alias() {
@Override
public Optional<String> name() {
return Optional.of(alias);
}

@Override
public Optional<String> password() {
return Optional.of(keyStoreConfig.password());
}
});

if (keyStore.isKeyEntry(alias)) {
Key key = keyStore.getKey(alias, aliasConfig.password().orElse(keyStoreConfig.password()).toCharArray());
properties.put(aliasConfig.name().orElse(alias), new String(key.getEncoded(), UTF_8));
} else if (keyStore.isCertificateEntry(alias)) {
// TODO
}
}
return new PropertiesConfigSource(properties, this.getName(), this.getOrdinal());
} catch (KeyStoreException | CertificateException | IOException | NoSuchAlgorithmException | UnrecoverableKeyException e) {
throw new RuntimeException(e);
}
}

@Override
public int getOrdinal() {
return ordinal;
}

@Override
public Set<String> getPropertyNames() {
throw new UnsupportedOperationException();
}

@Override
public String getValue(final String propertyName) {
throw new UnsupportedOperationException();
}

@Override
public String getName() {
return "KeyStoreConfigSource[source=" + url.toString() + "]";
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.smallrye.config.source.keystore.KeyStoreConfigSourceFactory
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.smallrye.config.source.keystore;

import io.smallrye.config.ConfigValue;
import io.smallrye.config.PropertiesConfigSource;
import io.smallrye.config.SmallRyeConfig;
import io.smallrye.config.SmallRyeConfigBuilder;
import org.junit.jupiter.api.Test;

import java.util.HashMap;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;

class KeyStoreConfigSourceTest {
@Test
void keystore() throws Exception {
Map<String, String> properties = new HashMap<>();
// keytool -importpass -alias my.secret -keystore keystore -storepass secret -storetype PKCS12 -v
properties.put("io.smallrye.config.source.keystore.test.path", "keystore");
properties.put("io.smallrye.config.source.keystore.test.password", "secret");

SmallRyeConfig config = new SmallRyeConfigBuilder()
.addDefaultInterceptors()
.addDiscoveredSources()
.withSources(new PropertiesConfigSource(properties, "", 0))
.build();

ConfigValue secret = config.getConfigValue("my.secret");
assertEquals("secret", secret.getValue());
}
}
Binary file added sources/keystore/src/test/resources/keystore
Binary file not shown.

0 comments on commit 350cc09

Please sign in to comment.