Skip to content

Commit

Permalink
client credentials workflow implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
wetret committed Jul 18, 2024
1 parent b33d447 commit 2f0e9a6
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import ca.uhn.fhir.context.FhirContext;
import de.medizininformatik_initiative.processes.common.fhir.client.logging.DataLogger;
import de.medizininformatik_initiative.processes.common.fhir.client.token.TokenProvider;
import de.rwh.utils.crypto.CertificateHelper;
import de.rwh.utils.crypto.io.CertificateReader;
import de.rwh.utils.crypto.io.PemIo;
Expand All @@ -38,6 +39,7 @@ public class FhirClientFactory
private final String fhirServerBasicAuthUsername;
private final String fhirServerBasicAuthPassword;
private final String fhirServerBearerToken;
private final TokenProvider fhirServerOAuth2TokenProvider;

private final String proxyUrl;
private final String proxyUsername;
Expand All @@ -54,8 +56,8 @@ public class FhirClientFactory
public FhirClientFactory(Path trustStorePath, Path certificatePath, Path privateKeyPath, char[] privateKeyPassword,
int connectTimeout, int socketTimeout, int connectionRequestTimeout, String fhirServerBase,
String fhirServerBasicAuthUsername, String fhirServerBasicAuthPassword, String fhirServerBearerToken,
String proxyUrl, String proxyUsername, String proxyPassword, boolean hapiClientVerbose,
FhirContext fhirContext, String localIdentifierValue, DataLogger dataLogger)
TokenProvider fhirServerOAuth2TokenProvider, String proxyUrl, String proxyUsername, String proxyPassword,
boolean hapiClientVerbose, FhirContext fhirContext, String localIdentifierValue, DataLogger dataLogger)
{
this.trustStorePath = trustStorePath;
this.certificatePath = certificatePath;
Expand All @@ -70,6 +72,7 @@ public FhirClientFactory(Path trustStorePath, Path certificatePath, Path private
this.fhirServerBasicAuthUsername = fhirServerBasicAuthUsername;
this.fhirServerBasicAuthPassword = fhirServerBasicAuthPassword;
this.fhirServerBearerToken = fhirServerBearerToken;
this.fhirServerOAuth2TokenProvider = fhirServerOAuth2TokenProvider;

this.proxyUrl = proxyUrl;
this.proxyUsername = proxyUsername;
Expand All @@ -85,11 +88,12 @@ public FhirClientFactory(Path trustStorePath, Path certificatePath, Path private

public void testConnection()
{
// TODO: log token provider configuration
try
{
logger.info(
"Testing connection to FHIR server with {trustStorePath: {}, certificatePath: {}, privateKeyPath: {}, privateKeyPassword: {},"
+ " basicAuthUsername {}, basicAuthPassword {}, bearerToken {}, serverBase: {}, proxyUrl {}, proxyUsername {}, proxyPassword {}}",
+ " basicAuthUsername: {}, basicAuthPassword: {}, bearerToken: {}, serverBase: {}, proxyUrl: {}, proxyUsername: {}, proxyPassword: {}}",
trustStorePath, certificatePath, privateKeyPath, privateKeyPassword != null ? "***" : "null",
fhirServerBasicAuthUsername, fhirServerBasicAuthPassword != null ? "***" : "null",
fhirServerBearerToken != null ? "***" : "null", fhirServerBase, proxyUrl, proxyUsername,
Expand Down Expand Up @@ -137,8 +141,8 @@ protected FhirClient createFhirClientImpl()

return new FhirClientImpl(trustStore, keyStore, keyStorePassword, connectTimeout, socketTimeout,
connectionRequestTimeout, fhirServerBasicAuthUsername, fhirServerBasicAuthPassword,
fhirServerBearerToken, fhirServerBase, proxyUrl, proxyUsername, proxyPassword, hapiClientVerbose,
fhirContext, localIdentifierValue, dataLogger);
fhirServerBearerToken, fhirServerOAuth2TokenProvider, fhirServerBase, proxyUrl, proxyUsername,
proxyPassword, hapiClientVerbose, fhirContext, localIdentifierValue, dataLogger);
}

private KeyStore readTrustStore(Path trustPath)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
import ca.uhn.fhir.rest.client.interceptor.BearerTokenAuthInterceptor;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.gclient.IReadTyped;
import de.medizininformatik_initiative.processes.common.fhir.client.interceptor.OAuth2Interceptor;
import de.medizininformatik_initiative.processes.common.fhir.client.logging.DataLogger;
import de.medizininformatik_initiative.processes.common.fhir.client.logging.HapiClientLogger;
import de.medizininformatik_initiative.processes.common.fhir.client.token.TokenProvider;

public class FhirClientImpl implements FhirClient
{
Expand All @@ -39,6 +41,7 @@ public class FhirClientImpl implements FhirClient
private final String fhirServerBasicAuthUsername;
private final String fhirServerBasicAuthPassword;
private final String fhirServerBearerToken;
private final TokenProvider fhirServerOAuth2TokenProvider;

private final boolean hapiClientVerbose;

Expand All @@ -50,9 +53,10 @@ public class FhirClientImpl implements FhirClient

public FhirClientImpl(KeyStore trustStore, KeyStore keyStore, char[] keyStorePassword, int connectTimeout,
int socketTimeout, int connectionRequestTimeout, String fhirServerBasicAuthUsername,
String fhirServerBasicAuthPassword, String fhirServerBearerToken, String fhirServerBase, String proxyUrl,
String proxyUsername, String proxyPassword, boolean hapiClientVerbose, FhirContext fhirContext,
String localIdentifierValue, DataLogger dataLogger)
String fhirServerBasicAuthPassword, String fhirServerBearerToken,
TokenProvider fhirServerOAuth2TokenProvider, String fhirServerBase, String proxyUrl, String proxyUsername,
String proxyPassword, boolean hapiClientVerbose, FhirContext fhirContext, String localIdentifierValue,
DataLogger dataLogger)
{
clientFactory = createClientFactory(trustStore, keyStore, keyStorePassword, connectTimeout, socketTimeout,
connectionRequestTimeout);
Expand All @@ -62,6 +66,7 @@ public FhirClientImpl(KeyStore trustStore, KeyStore keyStore, char[] keyStorePas
this.fhirServerBasicAuthUsername = fhirServerBasicAuthUsername;
this.fhirServerBasicAuthPassword = fhirServerBasicAuthPassword;
this.fhirServerBearerToken = fhirServerBearerToken;
this.fhirServerOAuth2TokenProvider = fhirServerOAuth2TokenProvider;

configureProxy(clientFactory, proxyUrl, proxyUsername, proxyPassword);

Expand Down Expand Up @@ -118,12 +123,18 @@ private void configuredWithBasicAuth(IGenericClient client)
new BasicAuthInterceptor(fhirServerBasicAuthUsername, fhirServerBasicAuthPassword));
}

private void configureBearerTokenAuthInterceptor(IGenericClient client)
private void configuredWithBearerTokenAuth(IGenericClient client)
{
if (fhirServerBearerToken != null)
client.registerInterceptor(new BearerTokenAuthInterceptor(fhirServerBearerToken));
}

private void configuredWithOAuth(IGenericClient client)
{
if (fhirServerOAuth2TokenProvider != null && fhirServerOAuth2TokenProvider.isConfigured())
client.registerInterceptor(new OAuth2Interceptor(fhirServerOAuth2TokenProvider));
}

private void configureLoggingInterceptor(IGenericClient client)
{
if (hapiClientVerbose)
Expand Down Expand Up @@ -158,7 +169,8 @@ public IGenericClient getGenericFhirClient()
IGenericClient client = clientFactory.newGenericClient(fhirServerBase);

configuredWithBasicAuth(client);
configureBearerTokenAuthInterceptor(client);
configuredWithBearerTokenAuth(client);
configuredWithOAuth(client);
configureLoggingInterceptor(client);

return client;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package de.medizininformatik_initiative.processes.common.fhir.client.interceptor;

import java.util.Objects;

import org.springframework.beans.factory.InitializingBean;

import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import de.medizininformatik_initiative.processes.common.fhir.client.token.TokenProvider;

public class OAuth2Interceptor implements IClientInterceptor, InitializingBean
{
private final TokenProvider tokenProvider;

public OAuth2Interceptor(TokenProvider tokenProvider)
{
this.tokenProvider = tokenProvider;
}

@Override
public void afterPropertiesSet()
{
Objects.requireNonNull(tokenProvider, "tokenProvider");
}

@Override
public void interceptRequest(IHttpRequest theRequest)
{
theRequest.addHeader(Constants.HEADER_AUTHORIZATION,
Constants.HEADER_AUTHORIZATION_VALPREFIX_BEARER + tokenProvider.getToken());
}

@Override
public void interceptResponse(IHttpResponse theResponse)
{
// do not intercept response
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package de.medizininformatik_initiative.processes.common.fhir.client.token;

import java.time.LocalDateTime;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class AccessToken
{
private static final int BUFFER = 10;

private final String token;

private final LocalDateTime expiresAt;

@JsonCreator
public AccessToken(@JsonProperty("access_token") String token, @JsonProperty("expires_in") int expiresIn)
{
this.token = token;
this.expiresAt = LocalDateTime.now().plusSeconds(expiresIn);
}

public String get()
{
return token;
}

public boolean isExpired()
{
return LocalDateTime.now().plusSeconds(BUFFER).isAfter(expiresAt);
}
}
Loading

0 comments on commit 2f0e9a6

Please sign in to comment.