Skip to content

Commit

Permalink
Merge pull request #122 from medizininformatik-initiative/release-1.0…
Browse files Browse the repository at this point in the history
….0.6

Release 1.0.0.6
  • Loading branch information
EmteZogaf authored Aug 12, 2024
2 parents 75838a1 + 9760926 commit 2a9e51d
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 35 deletions.
1 change: 1 addition & 0 deletions mii-process-feasibility/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ Besides the [common DSF settings controlled by different environment variables][
| CLIENT_STORE_AUTH_BEARER_TOKEN | Bearer token used for authentication against a client target. Do not prefix this with `Bearer `! | `null` |
| CLIENT_STORE_AUTH_BASIC_USERNAME | Username for basic authentication against a FHIR server client target. | `null` |
| CLIENT_STORE_AUTH_BASIC_PASSWORD | Password for basic authentication against a FHIR server client target. | `null` |
| CLIENT_STORE_AUTH_OAUTH_ISSUER_URL | Issuer URL of the OpenID Connect provider to gain access token for FHIR server client target. | `null` |
| CLIENT_STORE_AUTH_OAUTH_CLIENT_ID | Client ID for authentication against a OpenID Connect provider to gain access token for FHIR server client target. | `null` |
| CLIENT_STORE_AUTH_OAUTH_CLIENT_PASSWORD | Client Password for authentication against a OpenID Connect provider to gain access token for FHIR server client target. | `null` |
| CLIENT_STORE_AUTH_OAUTH_PROXY_HOST | Forward proxy host for connecting to OpenID Connect provider. | `null` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import com.nimbusds.oauth2.sdk.AccessTokenResponse;
import com.nimbusds.oauth2.sdk.ClientCredentialsGrant;
import com.nimbusds.oauth2.sdk.ParseException;
import com.nimbusds.oauth2.sdk.GeneralException;
import com.nimbusds.oauth2.sdk.TokenErrorResponse;
import com.nimbusds.oauth2.sdk.TokenRequest;
import com.nimbusds.oauth2.sdk.TokenResponse;
import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
import com.nimbusds.oauth2.sdk.auth.Secret;
import com.nimbusds.oauth2.sdk.http.HTTPRequest;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import org.joda.time.DateTime;

import java.io.IOException;
Expand All @@ -32,27 +34,23 @@ final class OAuthInterceptor implements IClientInterceptor {
private HTTPRequest tokenRequest;
private AccessToken token;
private DateTime tokenExpiry;
private Optional<Proxy> proxy;
private Optional<String> proxyAuthHeader;
private Issuer issuer;
private ClientSecretBasic clientAuth;

public OAuthInterceptor(String oauthClientId, String oauthClientSecret, String oauthTokenUrl,
public OAuthInterceptor(String oauthClientId, String oauthClientSecret, String oauthIssuerUrl,
Optional<String> proxyHost, Optional<Integer> proxyPort, Optional<String> proxyUsername,
Optional<String> proxyPassword) {
super();
ClientSecretBasic clientAuth = new ClientSecretBasic(new ClientID(oauthClientId),
new Secret(oauthClientSecret));
HTTPRequest request = new TokenRequest(URI.create(oauthTokenUrl), clientAuth, new ClientCredentialsGrant())
.toHTTPRequest();

if (proxyHost.isPresent() && proxyPort.isPresent()) {
Proxy proxy = new Proxy(Type.HTTP,
InetSocketAddress.createUnresolved(proxyHost.get(), proxyPort.get()));
request.setProxy(proxy);

if (proxyUsername.isPresent() && proxyPassword.isPresent()) {
request.setHeader(HEADER_PROXY_AUTHORIZATION,
generateBasicAuthHeader(proxyUsername.get(), proxyPassword.get()));
}
}
tokenRequest = request;
clientAuth = new ClientSecretBasic(new ClientID(oauthClientId), new Secret(oauthClientSecret));
issuer = new Issuer(oauthIssuerUrl);
proxy = proxyHost.map(
h -> proxyPort.map(
p -> new Proxy(Type.HTTP, InetSocketAddress.createUnresolved(h, p))).orElse(null));
proxyAuthHeader = proxyUsername.map(
n -> proxyPassword.map(
p -> generateBasicAuthHeader(n, p)).orElse(null));
}

private String generateBasicAuthHeader(String username, String password) {
Expand All @@ -63,7 +61,7 @@ private String generateBasicAuthHeader(String username, String password) {
public String getToken() {
if (token == null || tokenExpiry == null || tokenExpiry.isBefore(DateTime.now().plus(TOKEN_EXPIRY_THRESHOLD))) {
try {
TokenResponse response = TokenResponse.parse(tokenRequest.send());
TokenResponse response = TokenResponse.parse(getTokenRequest().send());
if (!response.indicatesSuccess()) {
TokenErrorResponse errorResponse = response.toErrorResponse();
throw new OAuth2ClientException(errorResponse.getErrorObject().getCode() + " - "
Expand All @@ -73,13 +71,36 @@ public String getToken() {

token = successResponse.getTokens().getAccessToken();
tokenExpiry = DateTime.now().plus(token.getLifetime() * 1000);
} catch (ParseException | IOException e) {
} catch (GeneralException | IOException e) {
throw new OAuth2ClientException("OAuth2 access token tokenRequest failed", e);
}
}
return token.getValue();
}

private HTTPRequest getTokenRequest() throws GeneralException, IOException {
if (tokenRequest == null) {
HTTPRequest request = new TokenRequest(getTokenUri(), clientAuth, new ClientCredentialsGrant())
.toHTTPRequest();
tokenRequest = setProxy(request);
}
return tokenRequest;
}

private URI getTokenUri() throws GeneralException, IOException {
return OIDCProviderMetadata.resolve(issuer, r -> { setProxy(r); }).getTokenEndpointURI();
}

private HTTPRequest setProxy(HTTPRequest request) {
if (proxy.isPresent()) {
request.setProxy(proxy.get());
if (proxyAuthHeader.isPresent()) {
request.setHeader(HEADER_PROXY_AUTHORIZATION, proxyAuthHeader.get());
}
}
return request;
}

@Override
public void interceptRequest(IHttpRequest theRequest) {
theRequest.addHeader(Constants.HEADER_AUTHORIZATION,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,13 @@ public class StoreClientSpringConfig {
@Value("${de.medizininformatik_initiative.feasibility_dsf_process.client.store.base_url:}")
private String storeBaseUrl;

@Value("${de.medizininformatik_initiative.feasibility_dsf_process.client.store.auth.oauth.client.secret:#{null}}")
@Value("${de.medizininformatik_initiative.feasibility_dsf_process.client.store.auth.oauth.client.password:#{null}}")
private String oauthClientSecret;

@Value("${de.medizininformatik_initiative.feasibility_dsf_process.client.store.auth.oauth.client.id:#{null}}")
private String oauthClientId;

@Value("${de.medizininformatik_initiative.feasibility_dsf_process.client.store.auth.oauth.token.url:#{null}}")
@Value("${de.medizininformatik_initiative.feasibility_dsf_process.client.store.auth.oauth.issuer.url:#{null}}")
private String oauthTokenUrl;

@Value("${de.medizininformatik_initiative.feasibility_dsf_process.client.store.auth.oauth.proxy.host:#{null}}")
Expand Down Expand Up @@ -101,8 +101,8 @@ IGenericClient client(@Qualifier("store-client") FhirContext fhirContext,
} else if (!isNullOrEmpty(oauthClientId) && !isNullOrEmpty(oauthClientSecret)
&& !isNullOrEmpty(oauthTokenUrl)) {
client.registerInterceptor(new OAuthInterceptor(oauthClientId, oauthClientSecret, oauthTokenUrl,
Optional.of(oauthProxyHost), Optional.of(oauthProxyPort), Optional.of(oauthProxyUsername),
Optional.of(oauthProxyPassword)));
Optional.ofNullable(oauthProxyHost), Optional.ofNullable(oauthProxyPort),
Optional.ofNullable(oauthProxyUsername), Optional.ofNullable(oauthProxyPassword)));
}

if (basicAuthUsername != null || basicAuthPassword != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class OAuthInterceptorIT {
protected static final Network DEFAULT_CONTAINER_NETWORK = Network.newNetwork();

@Container
public static KeycloakContainer keycloak = new KeycloakContainer("quay.io/keycloak/keycloak:24.0")
public static KeycloakContainer keycloak = new KeycloakContainer("quay.io/keycloak/keycloak:25.0")
.withNetwork(DEFAULT_CONTAINER_NETWORK)
.withNetworkAliases("keycloak")
.withAdminUsername("admin")
Expand Down Expand Up @@ -48,9 +48,8 @@ public class OAuthInterceptorIT {

@Test
public void getToken() {
String tokenUrl = "http://" + keycloak.getHost() + ":" + keycloak.getFirstMappedPort()
+ "/realms/test/protocol/openid-connect/token";
OAuthInterceptor interceptor = new OAuthInterceptor("account", "test", tokenUrl, Optional.empty(),
String issuerUrl = "http://" + keycloak.getHost() + ":" + keycloak.getFirstMappedPort() + "/realms/test";
OAuthInterceptor interceptor = new OAuthInterceptor("account", "test", issuerUrl, Optional.empty(),
Optional.empty(), Optional.empty(), Optional.empty());

String token = interceptor.getToken();
Expand All @@ -60,8 +59,8 @@ public void getToken() {

@Test
public void getTokenViaProxyNoAuth() {
String tokenUrl = "http://keycloak:8080/realms/test/protocol/openid-connect/token";
OAuthInterceptor interceptor = new OAuthInterceptor("account", "test", tokenUrl,
String issuerUrl = "http://keycloak:8080/realms/test";
OAuthInterceptor interceptor = new OAuthInterceptor("account", "test", issuerUrl,
Optional.of(forwardProxyNoAuth.getHost()),
Optional.of(forwardProxyNoAuth.getFirstMappedPort()),
Optional.empty(), Optional.empty());
Expand All @@ -73,8 +72,8 @@ public void getTokenViaProxyNoAuth() {

@Test
public void getTokenViaProxyBasicAuth() {
String tokenUrl = "http://keycloak:8080/realms/test/protocol/openid-connect/token";
OAuthInterceptor interceptor = new OAuthInterceptor("account", "test", tokenUrl,
String issuerUrl = "http://keycloak:8080/realms/test";
OAuthInterceptor interceptor = new OAuthInterceptor("account", "test", issuerUrl,
Optional.of(forwardProxyBasicAuth.getHost()),
Optional.of(forwardProxyBasicAuth.getFirstMappedPort()),
Optional.of("test"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class StoreClientIT {
private static final Network DEFAULT_CONTAINER_NETWORK = Network.newNetwork();

@Container
public GenericContainer<?> fhirServer = new GenericContainer<>(DockerImageName.parse("samply/blaze:0.24"))
public GenericContainer<?> fhirServer = new GenericContainer<>(DockerImageName.parse("samply/blaze:0.27"))
.withExposedPorts(8080)
.withNetwork(DEFAULT_CONTAINER_NETWORK)
.withNetworkAliases("fhir-server")
Expand Down
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<compileSource>17</compileSource>
<compileTarget>17</compileTarget>
<dsf.version>1.3.1</dsf.version>
<dsf.version>1.5.1</dsf.version>
<hapi.fhir.version>5.1.0</hapi.fhir.version>
<testcontainers.version>1.19.3</testcontainers.version>
<testcontainers.version>1.19.8</testcontainers.version>
</properties>

<name>MII Processes Feasibility</name>
Expand Down

0 comments on commit 2a9e51d

Please sign in to comment.