From 853dc53859d791e19b135fd3e06bcffe9eaba375 Mon Sep 17 00:00:00 2001 From: Jan Zickermann <jan.zickermann@dataport.de> Date: Fri, 8 Nov 2024 14:30:48 +0100 Subject: [PATCH] #2 it-case-setup: Check client_id in jwt body --- .../osiv2/OsiPostfachRemoteServiceITCase.java | 10 ++++- .../postfach/osiv2/extension/JwtParser.java | 37 +++++++++++++++++++ .../osiv2/extension/JwtParserTest.java | 19 ++++++++++ 3 files changed, 64 insertions(+), 2 deletions(-) create mode 100644 src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParser.java create mode 100644 src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParserTest.java diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java index 8723448..4633ec3 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java @@ -17,6 +17,7 @@ import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import de.ozgcloud.nachrichten.postfach.PostfachNachricht; +import de.ozgcloud.nachrichten.postfach.osiv2.extension.JwtParser; import de.ozgcloud.nachrichten.postfach.osiv2.extension.OsiMockServerExtension; @SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) @@ -60,9 +61,9 @@ public class OsiPostfachRemoteServiceITCase { @DisplayName("send message") @Nested class TestSendMessage { - @DisplayName("should send dummy request") + @DisplayName("should send dummy request with jwt") @Test - void shouldSendDummyRequest() { + void shouldSendDummyRequestWithJwt() { osiPostfachRemoteService.sendMessage(postfachNachricht); var requests = mockServerClient.retrieveRecordedRequests( @@ -71,6 +72,11 @@ public class OsiPostfachRemoteServiceITCase { .withPath("/dummy") ); assertThat(requests).hasSize(1); + String clientId = JwtParser.parseBody( + requests[0].getHeader("Authorization").getFirst() + ).read("$.client_id"); + assertThat(clientId).isEqualTo("OZG-Kopfstelle"); } + } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParser.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParser.java new file mode 100644 index 0000000..3135ab4 --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParser.java @@ -0,0 +1,37 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.extension; + +import org.eclipse.jgit.util.Base64; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.ReadContext; + +import lombok.SneakyThrows; + +public class JwtParser { + + @SneakyThrows + public static ReadContext parseBody(String authorizationHeaderValue) { + var jwt = authorizationHeaderValue.substring("Bearer ".length()); + // Step 2: Split the JWT + String[] jwtParts = jwt.split("\\."); + + if (jwtParts.length != 3) { + throw new IllegalArgumentException("Invalid JWT token"); + } + + // Step 3: Base64Url decode the Payload part + var payloadJson = base64UrlDecode(jwtParts[1]); + return JsonPath.parse(new String(payloadJson)); + } + + private static byte[] base64UrlDecode(String input) { + // Replace URL-safe characters + String base64 = input.replace('-', '+').replace('_', '/'); + // Add padding if necessary + int padding = 4 - (base64.length() % 4); + if (padding < 4) { + base64 += "=".repeat(padding); + } + return Base64.decode(base64); + } +} diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParserTest.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParserTest.java new file mode 100644 index 0000000..9e5ab35 --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParserTest.java @@ -0,0 +1,19 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.extension; + +import static org.assertj.core.api.Assertions.*; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class JwtParserTest { + + @DisplayName("should parse") + @Test + void shouldParse() { + var headerValue = "Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJ1aV9EaHVXUzdocFhzV3dZTHRlOHFIRkR4bnNFYldlVmJBZ0pzaWpsWGw4In0.eyJleHAiOjE3MzEwNzA5NTEsImlhdCI6MTczMTA3MDg5MSwianRpIjoiZTFjNWE4YjEtZWEyYS00Mzg5LTkyNDQtZWE5Mjc4M2IyZDA1IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDozMjkyNy9yZWFsbXMvbWFzdGVyIiwic3ViIjoiNTg1MzdjMGQtMzU3MS00MDExLWIxM2ItZDY1MGZjOGUwZjQ0IiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiT1pHLUtvcGZzdGVsbGUiLCJzY29wZSI6ImFjY2Vzc191cm46c29tZTpzY29wZTpmb3I6b3pna29wZnN0ZWxsZSBkZWZhdWx0IiwiY2xpZW50SG9zdCI6IjE3Mi4xNy4wLjEiLCJjbGllbnRBZGRyZXNzIjoiMTcyLjE3LjAuMSIsImNsaWVudF9pZCI6Ik9aRy1Lb3Bmc3RlbGxlIn0.MRGusCVssO-fHRp8-tEcdQWE7QVi3P0iHdmO4rGUwj_17KtHzQAT8ShZEVvE8oL-y-XKAPh7eT9will3oON1qhW6GHbZk5Xds4P5u8D0iHNl8nCSi_YS122v9Q1gwPrwPtVH26AKrdNM_YYv0AzT63gOVUoK4YY4jLhow3Uid2AVr2OMNAtcSPMysHXS1VeQRrhOm33JF_WVlguIHNjRpvRqCULkwywBRXDJm2mHOohkXFf10nM3ORAlmeElJCZa7Lg0zeg3q957Z9Mv5KbZA1X_QiHR5qpaDvimn0R_TTCZTGWM00GfyEHi2UU1s2ZfBeZTLOTNg2MUuDgA1cI7CQ"; + var context = JwtParser.parseBody(headerValue); + + String value = context.read("$.client_id"); + assertThat(value).isEqualTo("OZG-Kopfstelle"); + } +} -- GitLab