Skip to content

Commit

Permalink
Merge branch 'development' into main
Browse files Browse the repository at this point in the history
# Conflicts:
#	CHANGELOG.md
#	README.md
#	pom.xml
  • Loading branch information
tmberthold committed Jan 19, 2022
2 parents 500f737 + edd14eb commit 30be4c1
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 161 deletions.
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,29 @@
# Changelog
All notable changes to this project will be documented in this file.

## Version [6.0.0] 2022-01-19

### Major Change: dat issuer and public key kid are read from the received token
- Until now, the two application.properties variables `daps.key.url` and `daps.key.url.kid` were used to determine the issuer-url and the key-id (kid) under which the public key of the issuer of the DAT of a received message can be requested. This information is now dynamically read directly from the DAT received (header:kid, payload:iss), which are mandatory fields for a DAT. As a result, the two settings variables mentioned above are omitted. Due to the consequential changes, there are now major changes when using the `getClaims` method of the `DapsValidator`, since this method is no longer static and no longer needs to be passed the public key as a parameter. ([Issue 418](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/issues/418))

### Patch Change: Fixes
- When retrieving data from APIs outside the IDS context reusing the HTTP client of the Messaging-Services, there could be a problem with GZIP compressed API responses. An additional response interceptor has been added to handle all GZIP compressed responses, regardless of the details of the original request send. ([Issue 399](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/issues/399))

### Patch Change: Infomodel Maintenance
- Update combination of used artifacts: java (v4.2.7), serializer (v4.2.8), interaction (v4.2.7) ([PR 402](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/402))

### Patch Change: Dependency Maintenance
- Upgrade: org.springframework.boot:spring-boot-starter-test 2.6.1 -> 2.6.2 ([PR 410](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/410))
- Upgrade: org.springframework.boot:spring-boot-starter 2.6.1 -> 2.6.2 ([PR 410](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/410))
- Upgrade: org.springframework:spring-webmvc 5.3.13 -> 5.3.15 ([PR 400](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/400), [PR 417](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/417))
- Upgrade: org.springframework:spring-core 5.3.13 -> 5.3.15 ([PR 400](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/400), [PR 417](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/417))
- Upgrade: org.springframework:spring-web 5.3.13 -> 5.3.15 ([PR 400](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/400), [PR 417](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/417))
- Upgrade: org.springframework:spring-test 5.3.13 -> 5.3.15 ([PR 400](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/400), [PR 417](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/417))
- Upgrade: org.springframework:spring-tx 5.3.13 -> 5.3.15 ([PR 400](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/400), [PR 417](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/417))
- Upgrade: org.apache.jena:jena-core 4.3.0 -> 4.3.2 ([PR 393](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/393), [PR 410](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/410))
- Upgrade: com.puppycrawl.tools:checkstyle 9.2 -> 9.2.1 ([PR 410](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/410))
- Upgrade: maven-compiler-plugin 3.8.1 -> 3.9.0 ([PR 411](https://github.com/International-Data-Spaces-Association/IDS-Messaging-Services/pull/411))

## Version [5.3.0] 2021-12-13

### Minor Change: New application.properties flags
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ The following IDS-Infomodel-Artifacts are used as dependencies.

| Group | Artifact | Version |
| ------ | ------ | ------ |
| de.fraunhofer.iais.eis.ids.infomodel | java | 4.2.8 |
| de.fraunhofer.iais.eis.ids.infomodel | java | 4.2.7 |
| de.fraunhofer.iais.eis.ids | infomodel-serializer | 4.2.8 |
| de.fraunhofer.iais.eis.ids | interaction | 4.2.8 |
| de.fraunhofer.iais.eis.ids | interaction | 4.2.7 |

## Overview: Supported IDS-Message protocols

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ public void setUp() throws Exception{
Mockito.when(configurationContainer.getConfigurationModel()).thenReturn(configurationModel);
Mockito.when(configurationModel.getConnectorDeployMode()).thenReturn(ConnectorDeployMode.TEST_DEPLOYMENT);
Mockito.when(dapsTokenProvider.provideDapsToken()).thenReturn("Mocked Token.");
Mockito.when(dapsPublicKeyProvider.providePublicKeys()).thenReturn(null);
Mockito.when(dapsValidator.checkDat(fakeToken)).thenReturn(true);
Mockito.when(dapsValidator.checkDat(fakeToken)).thenReturn(true);
Mockito.when(dapsTokenProvider.getDAT()).thenReturn(fakeToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ public void setUp() throws Exception{
Mockito.when(configurationContainer.getConfigurationModel()).thenReturn(configurationModel);
Mockito.when(configurationModel.getConnectorDeployMode()).thenReturn(ConnectorDeployMode.TEST_DEPLOYMENT);
Mockito.when(dapsTokenProvider.provideDapsToken()).thenReturn("Mocked Token.");
Mockito.when(dapsPublicKeyProvider.providePublicKeys()).thenReturn(null);
Mockito.when(dapsValidator.checkDat(fakeToken)).thenReturn(true);
Mockito.when(dapsValidator.checkDat(fakeToken)).thenReturn(true);
Mockito.when(dapsTokenProvider.getDAT()).thenReturn(fakeToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ public void setUp() throws Exception{
Mockito.when(configurationContainer.getConfigurationModel()).thenReturn(configurationModel);
Mockito.when(configurationModel.getConnectorDeployMode()).thenReturn(ConnectorDeployMode.TEST_DEPLOYMENT);
Mockito.when(dapsTokenProvider.provideDapsToken()).thenReturn("Mocked Token.");
Mockito.when(dapsPublicKeyProvider.providePublicKeys()).thenReturn(null);
Mockito.when(dapsValidator.checkDat(fakeToken)).thenReturn(true);
Mockito.when(dapsValidator.checkDat(fakeToken)).thenReturn(true);
Mockito.when(dapsTokenProvider.getDAT()).thenReturn(fakeToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,14 @@
import lombok.extern.slf4j.Slf4j;
import okhttp3.Authenticator;
import okhttp3.Credentials;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Response;
import okhttp3.internal.http.RealResponseBody;
import okio.GzipSource;
import okio.Okio;
import org.jetbrains.annotations.NotNull;


/**
* The ClientProvider uses the {@link ConfigContainer} to rebuild
* clients, when a new configurationContainer is created.
Expand Down Expand Up @@ -122,7 +126,7 @@ private static void handleConnectorProxy(final ConfigurationModel connector,
if (connector.getConnectorProxy() != null) {
//if there is any proxy in the proxylist
final var proxyConfiguration = connector
.getConnectorProxy().stream().findAny().orElse(null);
.getConnectorProxy().stream().findAny().orElse(null);

if (proxyConfiguration != null) {
if (log.isDebugEnabled()) {
Expand Down Expand Up @@ -193,13 +197,13 @@ public List<Proxy> select(final URI uri) {
} else if (proxyPort == -1) {
if (log.isWarnEnabled()) {
log.warn("Proxy port invalid! Trying to skip using this proxy!"
+ " Please check configuration! [code=(IMSCOW0031),"
+ " Please check configuration! [code=(IMSCOW0031),"
+ " port=({})]", proxyPort);
}
proxyList.add(Proxy.NO_PROXY);
} else {
proxyList.add(new Proxy(Proxy.Type.HTTP,
new InetSocketAddress(proxyHost, proxyPort)));
new InetSocketAddress(proxyHost, proxyPort)));
}

}
Expand Down Expand Up @@ -239,11 +243,11 @@ private static void setProxyAuthenticator(

final Authenticator proxyAuthenticator = (route, response) -> {
final var credential = Credentials.basic(proxyConfiguration
.getProxyAuthentication()
.getAuthUsername(),
proxyConfiguration
.getProxyAuthentication()
.getAuthPassword());
.getProxyAuthentication()
.getAuthUsername(),
proxyConfiguration
.getProxyAuthentication()
.getAuthPassword());
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
Expand Down Expand Up @@ -354,7 +358,8 @@ private void setClient(final ConfigContainer configContainer)
this.client =
createClientBuilder(configContainer.getConfigurationModel(),
configContainer.getKeyStoreManager())
.build();
.addInterceptor(new EncodingInterceptor())
.build();
}

/**
Expand Down Expand Up @@ -390,9 +395,9 @@ public OkHttpClient getClientWithTimeouts(final Duration connectTimeout,
final Duration callTimeout) {

final var withTimeout =
rebuildClientWithTimeouts(client, connectTimeout,
readTimeout, writeTimeout,
callTimeout);
rebuildClientWithTimeouts(client, connectTimeout,
readTimeout, writeTimeout,
callTimeout);

if (log.isDebugEnabled()) {
log.debug("Ok Http Client Protocols: [code=(IMSCOD0076),"
Expand Down Expand Up @@ -463,4 +468,61 @@ private OkHttpClient rebuildClientWithTimeouts(final OkHttpClient client,

return okHttpClient;
}

/**
* Adds an interceptor to handle Content-Encodings in response header.
*/
private class EncodingInterceptor implements Interceptor {
@Override
@NotNull
public Response intercept(final Chain chain) throws IOException {
if (log.isDebugEnabled()) {
log.debug("EncodingInterceptor: Checking response encoding for gzip."
+ " [code=(IMSCOD0144)");
}

final var response = chain.proceed(chain.request());

return handleGzip(response);
}

private Response handleGzip(final Response response) {
final var bodyPresent = (response.body() != null);
final var gzipPresent = "gzip".equalsIgnoreCase(
response.headers().get("Content-Encoding"));

if (!bodyPresent || !gzipPresent) {
if (log.isDebugEnabled()) {
log.debug("EncodingInterceptor: No body in response or no gzip present."
+ " [code=(IMSCOD0145)");
}

return response;
} else {
if (log.isDebugEnabled()) {
log.debug("EncodingInterceptor: Response body present and"
+ " gzip encoded, uncompressing. [code=(IMSCOD0146)");
}

try {
final var responseBody = new GzipSource(response.body().source());
final var contentLength = response.body().contentLength();
final var strippedHeaders = response.headers()
.newBuilder()
.removeAll("Content-Encoding")
.build();

return response.newBuilder()
.headers(strippedHeaders)
.body(new RealResponseBody(
response.body().contentType().toString(),
contentLength,
Okio.buffer(responseBody)))
.build();
} catch (Exception e) {
return response;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@
package de.fraunhofer.ids.messaging.core.daps;

import java.security.Key;
import java.util.List;

/**
* An Implementation of this has to provide the Public Key from the DAPS Service.
*/
public interface DapsPublicKeyProvider {

/**
* Get the Public Key from the JWKS of the DAPS.
* Try to get the Public Key using the kid from jwks of issuer DAPS.
*
* @return The public Key of a DAPS Service.
* @param issuer Issuer of the DAT.
* @param kid KID of public key from jwks.
* @return publicKey of issuer DAPS (or null if it does not exist).
*/
List<Key> providePublicKeys();
Key requestPublicKey(String issuer, String kid);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@

import java.security.Key;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

import de.fraunhofer.iais.eis.DynamicAttributeToken;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -68,53 +68,55 @@ public class DapsValidator {
"idsc:TRUSTED_CONNECTOR_PLUS_SECURITY_PROFILE"};

/**
* Extract the Claims from the Dat token of a message, given the Message and a signingKey.
* Extract the Claims from the Dat token of a message, given the Message and a publicKey.
*
* @param token {@link DynamicAttributeToken} of an incoming RequestMessage.
* @param signingKeys A list of public Keys.
* @param publicKey The public Key.
* @return The Claims of the messages DAT Token, when it can be signed with the given key.
* @throws ClaimsException If Token cannot be signed with the given key.
*/
public static Jws<Claims> getClaims(final DynamicAttributeToken token,
final List<Key> signingKeys)
public Jws<Claims> getClaims(@NonNull final DynamicAttributeToken token,
@NonNull final Key publicKey)
throws ClaimsException {

final var tokenValue = token.getTokenValue();

if (signingKeys == null || signingKeys.isEmpty()) {
throw new ClaimsException("No signing keys were given!");
}

for (final var signingKey : signingKeys) {
try {
return Jwts.parser()
.setSigningKey(signingKey)
.parseClaimsJws(tokenValue);
} catch (Exception e) {
//nothing to do, exception is thrown below
try {
return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(tokenValue);
} catch (Exception e) {
if (log.isWarnEnabled()) {
log.warn(
"Claims could not be read from the token! [code=(IMSCOW0149),"
+ " message=({})]", e.getMessage());
}
}

throw new ClaimsException("No given signing Key could validate JWT!");
throw new ClaimsException("Claims could not be read from the token! [message=({})]");
}
}

/**
* Get the claims of the DAT.
* Get the claims of the DAT and validate it using the public key of the issuer.
*
* @param token Incoming DAT token.
* @return Claims extracted from the DAT.
* @throws ClaimsException If token cannot be parsed using a DAPS public key.
*/
public Jws<Claims> getClaims(final DynamicAttributeToken token) throws ClaimsException {
Jws<Claims> claims;
final var keys = keyProvider.providePublicKeys();

try {
claims = getClaims(token, keys);
return claims;
} catch (ClaimsException | ExpiredJwtException e) {
throw e;
}
//remove signature from token, since public key not yet available
//(used JWT-Parser requires public key otherwise)
final var noSigJwt = token.getTokenValue().substring(0,
token.getTokenValue().lastIndexOf('.') + 1);

//read contents of token without signature using the JWT-Parser
final var claim = Jwts.parser().parseClaimsJwt(noSigJwt);

//read kid and issuer of token content and request public key from issuer using the kid
final var key = keyProvider.requestPublicKey(
claim.getBody().getIssuer(),
claim.getHeader().get("kid").toString());

//get claims from token using token and requested public key of issuer
//(and validate the token using the public key as signingKey in parser)
return getClaims(token, key);
}

/**
Expand Down
Loading

0 comments on commit 30be4c1

Please sign in to comment.