Skip to content

Commit 86c5bb5

Browse files
committed
access to default client config trust store via new config provider
Adds new FhirClientConfigProvider to the API with access to client configs (moved from FhirClientProvider), the default client config trust store and a method to create a SSLContext based on the default trust store. New integration test for config provider.
1 parent 3044740 commit 86c5bb5

File tree

17 files changed

+557
-346
lines changed

17 files changed

+557
-346
lines changed

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/ProcessPluginApiImpl.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import dev.dsf.bpe.v2.service.DataLogger;
1313
import dev.dsf.bpe.v2.service.DsfClientProvider;
1414
import dev.dsf.bpe.v2.service.EndpointProvider;
15+
import dev.dsf.bpe.v2.service.FhirClientConfigProvider;
1516
import dev.dsf.bpe.v2.service.FhirClientProvider;
1617
import dev.dsf.bpe.v2.service.MailService;
1718
import dev.dsf.bpe.v2.service.MimeTypeService;
@@ -30,6 +31,7 @@ public class ProcessPluginApiImpl implements ProcessPluginApi, InitializingBean
3031
private final FhirContext fhirContext;
3132
private final DsfClientProvider dsfClientProvider;
3233
private final FhirClientProvider fhirClientProvider;
34+
private final FhirClientConfigProvider fhirClientConfigProvider;
3335
private final OidcClientProvider oidcClientProvider;
3436
private final MailService mailService;
3537
private final MimeTypeService mimeTypeService;
@@ -45,9 +47,9 @@ public class ProcessPluginApiImpl implements ProcessPluginApi, InitializingBean
4547

4648
public ProcessPluginApiImpl(ProxyConfig proxyConfig, EndpointProvider endpointProvider, FhirContext fhirContext,
4749
DsfClientProvider dsfClientProvider, FhirClientProvider fhirClientProvider,
48-
OidcClientProvider oidcClientProvider, MailService mailService, MimeTypeService mimeTypeService,
49-
ObjectMapper objectMapper, OrganizationProvider organizationProvider,
50-
ProcessAuthorizationHelper processAuthorizationHelper,
50+
FhirClientConfigProvider fhirClientConfigProvider, OidcClientProvider oidcClientProvider,
51+
MailService mailService, MimeTypeService mimeTypeService, ObjectMapper objectMapper,
52+
OrganizationProvider organizationProvider, ProcessAuthorizationHelper processAuthorizationHelper,
5153
QuestionnaireResponseHelper questionnaireResponseHelper, ReadAccessHelper readAccessHelper,
5254
TaskHelper taskHelper, CryptoService cryptoService, TargetProvider targetProvider, DataLogger dataLogger)
5355
{
@@ -56,6 +58,7 @@ public ProcessPluginApiImpl(ProxyConfig proxyConfig, EndpointProvider endpointPr
5658
this.fhirContext = fhirContext;
5759
this.dsfClientProvider = dsfClientProvider;
5860
this.fhirClientProvider = fhirClientProvider;
61+
this.fhirClientConfigProvider = fhirClientConfigProvider;
5962
this.oidcClientProvider = oidcClientProvider;
6063
this.mailService = mailService;
6164
this.mimeTypeService = mimeTypeService;
@@ -78,6 +81,7 @@ public void afterPropertiesSet() throws Exception
7881
Objects.requireNonNull(fhirContext, "fhirContext");
7982
Objects.requireNonNull(dsfClientProvider, "dsfClientProvider");
8083
Objects.requireNonNull(fhirClientProvider, "fhirClientProvider");
84+
Objects.requireNonNull(fhirClientConfigProvider, "fhirClientConfigProvider");
8185
Objects.requireNonNull(oidcClientProvider, "oidcClientProvider");
8286
Objects.requireNonNull(mailService, "mailService");
8387
Objects.requireNonNull(mimeTypeService, "mimeTypeService");
@@ -122,6 +126,12 @@ public FhirClientProvider getFhirClientProvider()
122126
return fhirClientProvider;
123127
}
124128

129+
@Override
130+
public FhirClientConfigProvider getFhirClientConfigProvider()
131+
{
132+
return fhirClientConfigProvider;
133+
}
134+
125135
@Override
126136
public OidcClientProvider getOidcClientProvider()
127137
{
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package dev.dsf.bpe.v2.service;
2+
3+
import java.io.ByteArrayInputStream;
4+
import java.io.ByteArrayOutputStream;
5+
import java.io.IOException;
6+
import java.security.KeyManagementException;
7+
import java.security.KeyStore;
8+
import java.security.KeyStoreException;
9+
import java.security.NoSuchAlgorithmException;
10+
import java.security.UnrecoverableKeyException;
11+
import java.security.cert.CertificateException;
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
import java.util.Objects;
15+
import java.util.Optional;
16+
import java.util.UUID;
17+
import java.util.function.Function;
18+
import java.util.stream.Collectors;
19+
20+
import javax.net.ssl.SSLContext;
21+
22+
import org.springframework.beans.factory.InitializingBean;
23+
24+
import de.hsheilbronn.mi.utils.crypto.context.SSLContextFactory;
25+
import dev.dsf.bpe.v2.client.fhir.ClientConfig;
26+
import dev.dsf.bpe.v2.client.fhir.ClientConfigs;
27+
28+
public class FhirClientConfigProviderImpl implements FhirClientConfigProvider, InitializingBean
29+
{
30+
private final Map<String, ClientConfig> clientConfigsByFhirServerId = new HashMap<>();
31+
private final KeyStore defaultTrustStore;
32+
33+
public FhirClientConfigProviderImpl(KeyStore defaultTrustStore, ClientConfigs clientConfigs)
34+
{
35+
this.defaultTrustStore = defaultTrustStore;
36+
37+
if (clientConfigs != null)
38+
clientConfigsByFhirServerId.putAll(clientConfigs.getConfigs().stream()
39+
.collect(Collectors.toMap(ClientConfig::getFhirServerId, Function.identity())));
40+
}
41+
42+
@Override
43+
public void afterPropertiesSet() throws Exception
44+
{
45+
Objects.requireNonNull(defaultTrustStore, "defaultTrustStore");
46+
}
47+
48+
@Override
49+
public SSLContext createDefaultSslContext()
50+
{
51+
try
52+
{
53+
return SSLContextFactory.createSSLContext(defaultTrustStore);
54+
}
55+
catch (UnrecoverableKeyException | KeyManagementException | KeyStoreException | NoSuchAlgorithmException e)
56+
{
57+
throw new RuntimeException(e);
58+
}
59+
}
60+
61+
@Override
62+
public KeyStore createDefaultTrustStore()
63+
{
64+
try
65+
{
66+
char[] password = UUID.randomUUID().toString().toCharArray();
67+
ByteArrayOutputStream out = new ByteArrayOutputStream();
68+
defaultTrustStore.store(out, password);
69+
70+
KeyStore store = KeyStore.getInstance(defaultTrustStore.getType(), defaultTrustStore.getProvider());
71+
store.load(new ByteArrayInputStream(out.toByteArray()), password);
72+
73+
return store;
74+
}
75+
catch (KeyStoreException | NoSuchAlgorithmException | CertificateException | IOException e)
76+
{
77+
throw new RuntimeException(e);
78+
}
79+
}
80+
81+
@Override
82+
public Optional<ClientConfig> getClientConfig(String fhirServerId)
83+
{
84+
if (fhirServerId == null || fhirServerId.isBlank())
85+
return Optional.empty();
86+
87+
return Optional.ofNullable(clientConfigsByFhirServerId.get(fhirServerId));
88+
}
89+
}
Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,36 @@
22

33
import java.security.KeyStore;
44
import java.time.Duration;
5-
import java.util.Objects;
65
import java.util.Optional;
76
import java.util.function.Function;
87

9-
import org.springframework.beans.factory.InitializingBean;
8+
import javax.net.ssl.SSLContext;
109

11-
import ca.uhn.fhir.rest.client.api.IGenericClient;
1210
import dev.dsf.bpe.api.config.FhirClientConfig;
1311
import dev.dsf.bpe.v2.client.fhir.ClientConfig;
1412

15-
public class FhirClientProviderWithEndpointSupport implements FhirClientProvider, InitializingBean
13+
public class FhirClientConfigProviderWithEndpointSupport implements FhirClientConfigProvider
1614
{
1715
private final EndpointProvider endpointProvider;
18-
private final FhirClientProviderImpl delegate;
16+
private final FhirClientConfigProvider delegate;
1917

20-
public FhirClientProviderWithEndpointSupport(EndpointProvider endpointProvider, FhirClientProviderImpl delegate)
18+
public FhirClientConfigProviderWithEndpointSupport(EndpointProvider endpointProvider,
19+
FhirClientConfigProvider delegate)
2120
{
2221
this.endpointProvider = endpointProvider;
2322
this.delegate = delegate;
2423
}
2524

2625
@Override
27-
public void afterPropertiesSet() throws Exception
26+
public SSLContext createDefaultSslContext()
2827
{
29-
Objects.requireNonNull(endpointProvider, "endpointProvider");
30-
Objects.requireNonNull(delegate, "delegate");
28+
return delegate.createDefaultSslContext();
3129
}
3230

3331
@Override
34-
public Optional<IGenericClient> getClient(String fhirServerId)
32+
public KeyStore createDefaultTrustStore()
3533
{
36-
if (fhirServerId == null || fhirServerId.isBlank())
37-
return Optional.empty();
38-
else if (fhirServerId.startsWith("#"))
39-
return getClientConfig(fhirServerId).flatMap(delegate::getClient);
40-
else
41-
return delegate.getClient(fhirServerId);
34+
return delegate.createDefaultTrustStore();
4235
}
4336

4437
@Override

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/service/FhirClientProviderImpl.java

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,12 @@
44
import java.util.Map;
55
import java.util.Objects;
66
import java.util.Optional;
7-
import java.util.function.Function;
8-
import java.util.stream.Collectors;
97

108
import org.springframework.beans.factory.InitializingBean;
119

1210
import ca.uhn.fhir.context.FhirContext;
1311
import ca.uhn.fhir.rest.client.api.IGenericClient;
1412
import dev.dsf.bpe.v2.client.fhir.ClientConfig;
15-
import dev.dsf.bpe.v2.client.fhir.ClientConfigs;
1613
import dev.dsf.bpe.v2.client.fhir.FhirClientFactory;
1714
import dev.dsf.bpe.v2.config.ProxyConfig;
1815

@@ -22,8 +19,8 @@ public class FhirClientProviderImpl implements FhirClientProvider, InitializingB
2219
private final ProxyConfig proxyConfig;
2320
private final OidcClientProvider oidcClientProvider;
2421
private final String userAgent;
22+
private final FhirClientConfigProvider configProvider;
2523

26-
private final Map<String, ClientConfig> clientConfigsByFhirServerId = new HashMap<>();
2724
private final Map<String, FhirClientFactory> clientFactoriesByFhirServerId = new HashMap<>();
2825

2926
/**
@@ -35,20 +32,17 @@ public class FhirClientProviderImpl implements FhirClientProvider, InitializingB
3532
* not <code>null</code>
3633
* @param userAgent
3734
* not <code>null</code>
38-
* @param clientConfigs
39-
* may be <code>null</code>
35+
* @param configProvider
36+
* not <code>null</code>
4037
*/
4138
public FhirClientProviderImpl(FhirContext fhirContext, ProxyConfig proxyConfig,
42-
OidcClientProvider oidcClientProvider, String userAgent, ClientConfigs clientConfigs)
39+
OidcClientProvider oidcClientProvider, String userAgent, FhirClientConfigProvider configProvider)
4340
{
4441
this.fhirContext = fhirContext;
4542
this.proxyConfig = proxyConfig;
4643
this.oidcClientProvider = oidcClientProvider;
4744
this.userAgent = userAgent;
48-
49-
if (clientConfigs != null)
50-
clientConfigsByFhirServerId.putAll(clientConfigs.getConfigs().stream()
51-
.collect(Collectors.toMap(ClientConfig::getFhirServerId, Function.identity())));
45+
this.configProvider = configProvider;
5246
}
5347

5448
@Override
@@ -58,6 +52,7 @@ public void afterPropertiesSet() throws Exception
5852
Objects.requireNonNull(proxyConfig, "proxyConfig");
5953
Objects.requireNonNull(oidcClientProvider, "oidcClientProvider");
6054
Objects.requireNonNull(userAgent, "userAgent");
55+
Objects.requireNonNull(configProvider, "configProvider");
6156
}
6257

6358
protected Optional<IGenericClient> getClient(ClientConfig clientConfig)
@@ -82,16 +77,7 @@ protected Optional<IGenericClient> getClient(ClientConfig clientConfig)
8277
@Override
8378
public Optional<IGenericClient> getClient(String fhirServerId)
8479
{
85-
return getClientConfig(fhirServerId).flatMap(this::getClient);
86-
}
87-
88-
@Override
89-
public Optional<ClientConfig> getClientConfig(String fhirServerId)
90-
{
91-
if (fhirServerId == null || fhirServerId.isBlank())
92-
return Optional.empty();
93-
94-
return Optional.ofNullable(clientConfigsByFhirServerId.get(fhirServerId));
80+
return configProvider.getClientConfig(fhirServerId).flatMap(this::getClient);
9581
}
9682

9783
protected FhirClientFactory createClientFactory(ClientConfig config)

dsf-bpe/dsf-bpe-process-api-v2-impl/src/main/java/dev/dsf/bpe/v2/spring/ApiServiceConfig.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@
4545
import dev.dsf.bpe.v2.service.DsfClientProviderImpl;
4646
import dev.dsf.bpe.v2.service.EndpointProvider;
4747
import dev.dsf.bpe.v2.service.EndpointProviderImpl;
48+
import dev.dsf.bpe.v2.service.FhirClientConfigProvider;
49+
import dev.dsf.bpe.v2.service.FhirClientConfigProviderImpl;
50+
import dev.dsf.bpe.v2.service.FhirClientConfigProviderWithEndpointSupport;
4851
import dev.dsf.bpe.v2.service.FhirClientProvider;
4952
import dev.dsf.bpe.v2.service.FhirClientProviderImpl;
50-
import dev.dsf.bpe.v2.service.FhirClientProviderWithEndpointSupport;
5153
import dev.dsf.bpe.v2.service.MailService;
5254
import dev.dsf.bpe.v2.service.MailServiceDelegate;
5355
import dev.dsf.bpe.v2.service.MimeTypeService;
@@ -101,9 +103,10 @@ public class ApiServiceConfig
101103
public ProcessPluginApi processPluginApiV2()
102104
{
103105
return new ProcessPluginApiImpl(proxyConfigDelegate(), endpointProvider(), fhirContext(), dsfClientProvider(),
104-
fhirClientProvider(), oidcClientProvider(), mailService(), mimeTypeService(), objectMapper(),
105-
organizationProvider(), processAuthorizationHelper(), questionnaireResponseHelper(), readAccessHelper(),
106-
taskHelper(), cryptoService(), targetProvider(), dataLogger());
106+
fhirClientProvider(), fhirClientConfigProvider(), oidcClientProvider(), mailService(),
107+
mimeTypeService(), objectMapper(), organizationProvider(), processAuthorizationHelper(),
108+
questionnaireResponseHelper(), readAccessHelper(), taskHelper(), cryptoService(), targetProvider(),
109+
dataLogger());
107110
}
108111

109112
@Bean
@@ -144,9 +147,15 @@ public DsfClientProvider dsfClientProvider()
144147
@Bean
145148
public FhirClientProvider fhirClientProvider()
146149
{
147-
return new FhirClientProviderWithEndpointSupport(endpointProvider(),
148-
new FhirClientProviderImpl(fhirContext(), proxyConfigDelegate(), oidcClientProvider(),
149-
buildInfoProvider.getUserAgentValue(), clientConfigsDelegate()));
150+
return new FhirClientProviderImpl(fhirContext(), proxyConfigDelegate(), oidcClientProvider(),
151+
buildInfoProvider.getUserAgentValue(), fhirClientConfigProvider());
152+
}
153+
154+
@Bean
155+
public FhirClientConfigProvider fhirClientConfigProvider()
156+
{
157+
return new FhirClientConfigProviderWithEndpointSupport(endpointProvider(),
158+
new FhirClientConfigProviderImpl(fhirClientConfigs.defaultTrustStore(), clientConfigsDelegate()));
150159
}
151160

152161
@Bean

dsf-bpe/dsf-bpe-process-api-v2/src/main/java/dev/dsf/bpe/v2/ProcessPluginApi.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import dev.dsf.bpe.v2.service.DataLogger;
1212
import dev.dsf.bpe.v2.service.DsfClientProvider;
1313
import dev.dsf.bpe.v2.service.EndpointProvider;
14+
import dev.dsf.bpe.v2.service.FhirClientConfigProvider;
1415
import dev.dsf.bpe.v2.service.FhirClientProvider;
1516
import dev.dsf.bpe.v2.service.MailService;
1617
import dev.dsf.bpe.v2.service.MimeTypeService;
@@ -41,6 +42,8 @@ public interface ProcessPluginApi
4142

4243
FhirClientProvider getFhirClientProvider();
4344

45+
FhirClientConfigProvider getFhirClientConfigProvider();
46+
4447
OidcClientProvider getOidcClientProvider();
4548

4649
MailService getMailService();
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package dev.dsf.bpe.v2.service;
2+
3+
import java.security.KeyStore;
4+
import java.util.Optional;
5+
6+
import javax.net.ssl.SSLContext;
7+
8+
import org.hl7.fhir.r4.model.Endpoint;
9+
10+
import dev.dsf.bpe.v2.client.fhir.ClientConfig;
11+
import dev.dsf.bpe.v2.constants.NamingSystems;
12+
13+
/**
14+
* Provides connection configurations for YAML configured (non DSF) FHIR servers and DSF FHIR servers, as well as access
15+
* to the default certificate trust store for FHIR connections configured via the DSF BPE property
16+
* `dev.dsf.bpe.fhir.client.connections.config.default.trust.server.certificate.cas` as default for the YAML properties
17+
* `trusted-root-certificates-file` and `oidc-auth.trusted-root-certificates-file`
18+
*/
19+
public interface FhirClientConfigProvider
20+
{
21+
/**
22+
* @return new {@link SSLContext} configured with {@link #createDefaultTrustStore()}
23+
* @implNote Every call to this method creates a new {@link SSLContext} object
24+
*/
25+
SSLContext createDefaultSslContext();
26+
27+
/**
28+
* @return copy of default certificate trust store configured via the DSF BPE config property
29+
* `dev.dsf.bpe.fhir.client.connections.config.default.trust.server.certificate.cas`
30+
* @implNote Every call to this method creates a new {@link KeyStore} object
31+
*/
32+
KeyStore createDefaultTrustStore();
33+
34+
/**
35+
* FHIR client config for a FHIR server configured via YAML with the given <b>fhirServerId</b>.<br>
36+
* <br>
37+
* Use <code>#local</code> as the <b>fhirServerId</b> for a connection configuration to the local DSF FHIR
38+
* server.<br>
39+
* Use <code>#&lt;value></code> as the <b>fhirServerId</b> for a connection configuration to a DSF FHIR server with
40+
* an active {@link Endpoint} resource and the given <b>fhirServerId</b> as the
41+
* {@value NamingSystems.EndpointIdentifier#SID} value (ignoring the {@literal #} character).
42+
*
43+
* @param fhirServerId
44+
* may be <code>null</code>
45+
* @return never <code>null</code>, {@link Optional#empty()} if no client is configured for the given
46+
* <b>fhirServerId</b>
47+
* @see DsfClientProvider
48+
*/
49+
Optional<ClientConfig> getClientConfig(String fhirServerId);
50+
}

0 commit comments

Comments
 (0)