diff --git a/pom.xml b/pom.xml index b8874701f0eb9377c132afa254d160877636b402..17cf614960bac4f12bd6922ee65e9a8bd3b3fe02 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,6 @@ <properties> <api-lib.version>0.13.0</api-lib.version> <nachrichten-manager.version>2.14.0</nachrichten-manager.version> - <testcontainers-keycloak.version>3.2.0</testcontainers-keycloak.version> <mockserver-client.version>5.15.0</mockserver-client.version> </properties> <dependencies> @@ -61,12 +60,6 @@ <type>test-jar</type> <scope>test</scope> </dependency> - <dependency> - <groupId>com.github.dasniko</groupId> - <artifactId>testcontainers-keycloak</artifactId> - <version>${testcontainers-keycloak.version}</version> - <scope>test</scope> - </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> diff --git a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/config/WebClientConfiguration.java b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/config/WebClientConfiguration.java index 454f52e0b89cf51700af6a0ed6a723972c1958e7..f851b7842e23623e1433f12c91d9e741a4028c98 100644 --- a/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/config/WebClientConfiguration.java +++ b/src/main/java/de/ozgcloud/nachrichten/postfach/osiv2/config/WebClientConfiguration.java @@ -8,18 +8,26 @@ import org.springframework.context.annotation.Primary; import org.springframework.core.env.Environment; import org.springframework.security.oauth2.client.AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.InMemoryReactiveOAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProvider; import org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientProviderBuilder; +import org.springframework.security.oauth2.client.endpoint.WebClientReactiveClientCredentialsTokenResponseClient; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; import org.springframework.security.oauth2.client.web.reactive.function.client.ServerOAuth2AuthorizedClientExchangeFilterFunction; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.client.WebClient; +import lombok.RequiredArgsConstructor; + @Configuration +@RequiredArgsConstructor public class WebClientConfiguration { + private final Environment environment; + @Bean("osi2PostfachWebClient") public WebClient osi2PostfachWebClient( - ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction, - Environment environment) { + ServerOAuth2AuthorizedClientExchangeFilterFunction serverOAuth2AuthorizedClientExchangeFilterFunction) { var url = Objects.requireNonNull( environment.getProperty("ozgcloud.osiv2-postfach.api.url"), "ozgcloud.osiv2-postfach.api.url is not set"); @@ -46,12 +54,33 @@ public class WebClientConfiguration { var authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager( clientRegistrations, clientService); - authorizedClientManager.setAuthorizedClientProvider( - ReactiveOAuth2AuthorizedClientProviderBuilder.builder() - .clientCredentials() - .build()); + authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider()); return authorizedClientManager; } + ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider() { + return ReactiveOAuth2AuthorizedClientProviderBuilder.builder() + .clientCredentials(builder -> + builder.accessTokenResponseClient(getClientCredentialsTokenResponseClient()) + ) + .build(); + } + + WebClientReactiveClientCredentialsTokenResponseClient getClientCredentialsTokenResponseClient() { + var client = new WebClientReactiveClientCredentialsTokenResponseClient(); + var resource = Objects.requireNonNull( + environment.getProperty("ozgcloud.osiv2-postfach.api.resource"), + "ozgcloud.osiv2-postfach.api.resource is not set" + ); + + client.addParametersConverter(source -> { + MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>(); + // Pass a resource indicator parameter https://datatracker.ietf.org/doc/html/rfc8707 + parameters.add("resource", resource); + return parameters; + }); + return client; + } + } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index a93d8dad377c3600a25688a2d493602a3e1ba5d8..879647a6304d7ea80e852958f21b4ef5cc3127bc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -12,10 +12,19 @@ spring: client-secret: 'changeme' scope: default, access_urn:some:scope:for:ozgkopfstelle authorization-grant-type: 'client_credentials' + client-authentication-method: client_secret_post provider: osi2: - token-uri: http://localhost:8080/realms/master/protocol/openid-connect/token + token-uri: 'https://idp.serviceportal-stage.schleswig-holstein.de/webidp2/connect/token' ozgcloud: osiv2-postfach: + enabled: true api: - url: 'replaceme' + resource: 'urn:dataport:osi:postfach:rz2:stage:sh' + url: 'https://api-gateway-stage.dataport.de:443/api/osi_postfach/1.0.0' + tenant: 'SH' + name-identifier: 'ozgkopfstelle' + http-proxy: + host: 10.65.108.2 + port: 3128 + authentication-required: false 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 4633ec32a3c03cf9491201380ffdf23ef4e73736..d622702767b754ce43e833ad5ee2b9e93ccf2f6a 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/OsiPostfachRemoteServiceITCase.java @@ -1,8 +1,13 @@ package de.ozgcloud.nachrichten.postfach.osiv2; +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.JwtFactory.*; import static org.assertj.core.api.Assertions.*; +import static org.mockserver.matchers.Times.*; +import static org.mockserver.model.Header.*; import static org.mockserver.model.HttpRequest.*; import static org.mockserver.model.HttpResponse.*; +import static org.mockserver.model.Parameter.*; +import static org.mockserver.model.ParameterBody.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -10,15 +15,16 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.mockserver.client.MockServerClient; -import org.mockserver.matchers.Times; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; 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.Jwt; import de.ozgcloud.nachrichten.postfach.osiv2.extension.OsiMockServerExtension; +import de.ozgcloud.nachrichten.postfach.osiv2.factory.JwtFactory; +import lombok.SneakyThrows; @SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) public class OsiPostfachRemoteServiceITCase { @@ -36,46 +42,83 @@ public class OsiPostfachRemoteServiceITCase { @DynamicPropertySource static void dynamicProperties(DynamicPropertyRegistry registry) { - registry.add("spring.security.oauth2.client.provider.osi2.token-uri", OSI_MOCK_SERVER_EXTENSION::getTokenUri); - registry.add("ozgcloud.osiv2-postfach.api.url", OSI_MOCK_SERVER_EXTENSION::getPostfachMockServerUrl); + registry.add("spring.security.oauth2.client.provider.osi2.token-uri", OSI_MOCK_SERVER_EXTENSION::getAccessTokenUrl); + registry.add("spring.security.oauth2.client.registration.osi2.scope", () -> CLIENT_SCOPES); + registry.add("spring.security.oauth2.client.registration.osi2.client-id", () -> CLIENT_ID); + registry.add("ozgcloud.osiv2-postfach.api.url", OSI_MOCK_SERVER_EXTENSION::getPostfachFacadeUrl); + registry.add("ozgcloud.osiv2-postfach.api.resource", () -> RESOURCE_URN); } - private MockServerClient mockServerClient; + private MockServerClient mockClient; @BeforeEach + @SneakyThrows public void setup() { - mockServerClient = OSI_MOCK_SERVER_EXTENSION.getMockServerClient(); - mockServerClient + mockClient = OSI_MOCK_SERVER_EXTENSION.getMockClient(); + + mockClient .when( request() - .withMethod("GET") - .withPath("/dummy"), - Times.exactly(1) + .withMethod("POST") + .withPath("/access-token") + .withHeaders( + header("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8") + ) + .withBody( + params( + param("grant_type", "client_credentials"), + param("client_id", CLIENT_ID), + param("client_secret", "changeme"), + param("scope", String.join(" ", CLIENT_SCOPES)), + param("resource", RESOURCE_URN) + ) + ), + exactly(1) ) .respond( response() .withStatusCode(200) + .withHeader("Content-Type", "application/json") + .withBody( + JwtFactory.createTokenResponse( + JwtFactory.createAccessTokenExampleWithExpireIn(900) + ) + ) ); } @DisplayName("send message") @Nested class TestSendMessage { + @DisplayName("should send dummy request with jwt") @Test void shouldSendDummyRequestWithJwt() { + mockClient + .when( + request() + .withMethod("GET") + .withPath("/dummy"), + exactly(1) + ) + .respond( + response() + .withStatusCode(200) + ); + osiPostfachRemoteService.sendMessage(postfachNachricht); - var requests = mockServerClient.retrieveRecordedRequests( + var requests = mockClient.retrieveRecordedRequests( request() .withMethod("GET") .withPath("/dummy") ); assertThat(requests).hasSize(1); - String clientId = JwtParser.parseBody( + var jwt = Jwt.parseAuthHeaderValue( requests[0].getHeader("Authorization").getFirst() - ).read("$.client_id"); - assertThat(clientId).isEqualTo("OZG-Kopfstelle"); + ); + assertThat(jwt.body().read("$.client_id", String.class)).isEqualTo(CLIENT_ID); + assertThat(jwt.body().read("$.aud", String.class)).isEqualTo(RESOURCE_URN); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/Jwt.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/Jwt.java new file mode 100644 index 0000000000000000000000000000000000000000..7d029e6b02e85f408adc03b3efd382e81cb13cf9 --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/Jwt.java @@ -0,0 +1,89 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.extension; + +import static de.ozgcloud.nachrichten.postfach.osiv2.factory.JsonUtil.*; + +import java.io.Serializable; +import java.util.Map; + +import org.eclipse.jgit.util.Base64; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.ReadContext; + +import lombok.SneakyThrows; + +public record Jwt(String token) { + + public record JwtParts(ReadContext header, ReadContext body, byte[] signature) { + } + + @SneakyThrows + public static JwtParts parseAuthHeaderValue(String authorizationHeaderValue) { + return new Jwt(discardBearerPrefix(authorizationHeaderValue)).parse(); + + } + + public String scope() { + return parse().body().read("$.scope"); + } + + public Integer issuedAt() { + return parse().body().read("$.iat", Integer.class); + } + + public Integer expireTime() { + return parse().body().read("$.exp", Integer.class); + } + + public JwtParts parse() { + var jwtParts = splitIntoHeaderAndBodyAndSignature(this.token); + return new JwtParts( + parseJsonPath(base64UrlDecode(jwtParts[0])), + parseJsonPath(base64UrlDecode(jwtParts[1])), + base64UrlDecode(jwtParts[2]) + ); + + } + + private static ReadContext parseJsonPath(byte[] bytes) { + return JsonPath.parse(new String(bytes)); + } + + private static String discardBearerPrefix(String authorizationHeaderValue) { + return authorizationHeaderValue.substring("Bearer ".length()); + } + + private static String[] splitIntoHeaderAndBodyAndSignature(String jwt) { + var jwtParts = jwt.split("\\."); + if (jwtParts.length != 3) { + throw new IllegalArgumentException("Invalid JWT token"); + } + return jwtParts; + } + + 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); + } + + @SneakyThrows + public static Jwt create(Map<String, Serializable> header, Map<String, Serializable> body, byte[] signature) { + var headerJson = toJson(header); + var bodyJson = toJson(body); + var token = base64UrlEncode(headerJson) + "." + base64UrlEncode(bodyJson) + "." + base64UrlEncode(new String(signature)); + return new Jwt(token); + } + + private static String base64UrlEncode(String input) { + String base64 = Base64.encodeBytes(input.getBytes()); + return base64.replace('+', '-').replace('/', '_').replace("=", ""); + + } + +} 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 deleted file mode 100644 index 85c18edb1889ecdebdb31dc6f32317df77594ffc..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParser.java +++ /dev/null @@ -1,47 +0,0 @@ -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 jwtParts = splitIntoSignatureAndHeaderAndBody( - discardBearerPrefix(authorizationHeaderValue) - ); - var bodyPart = jwtParts[1]; - return parseJsonPartFromUrlEncodedBase64(bodyPart); - } - - private static ReadContext parseJsonPartFromUrlEncodedBase64(String base64EncodedPayload) { - return JsonPath.parse(new String(base64UrlDecode(base64EncodedPayload))); - } - - private static String discardBearerPrefix(String authorizationHeaderValue) { - return authorizationHeaderValue.substring("Bearer ".length()); - } - - private static String[] splitIntoSignatureAndHeaderAndBody(String jwt) { - var jwtParts = jwt.split("\\."); - if (jwtParts.length != 3) { - throw new IllegalArgumentException("Invalid JWT token"); - } - return jwtParts; - } - - 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/JwtTest.java similarity index 66% rename from src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParserTest.java rename to src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtTest.java index 9e5ab356ae8e7447395cecbd7839a42d0bd83820..0552893306658033825e6d21df1714a18e392a37 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtParserTest.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/JwtTest.java @@ -2,18 +2,35 @@ package de.ozgcloud.nachrichten.postfach.osiv2.extension; import static org.assertj.core.api.Assertions.*; +import java.util.Map; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -class JwtParserTest { +class JwtTest { @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); + var context = Jwt.parseAuthHeaderValue(headerValue); - String value = context.read("$.client_id"); + String value = context.body().read("$.client_id"); assertThat(value).isEqualTo("OZG-Kopfstelle"); } + + @DisplayName("should create") + @Test + void shouldCreate() { + var jwt = Jwt.create(Map.of( + "alg", "RS256" + ), Map.of( + "sub", "58537c0d-3571-4011-b13b-d650fc8e0f44" + ), "signature".getBytes()); + + var jwtParts = jwt.parse(); + assertThat(jwtParts.header().read("$.alg", String.class)).isEqualTo("RS256"); + assertThat(jwtParts.body().read("$.sub", String.class)).isEqualTo("58537c0d-3571-4011-b13b-d650fc8e0f44"); + assertThat(jwtParts.signature()).isEqualTo("signature".getBytes()); + } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/OsiMockServerExtension.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/OsiMockServerExtension.java index ad7c5601652fb2bd9ba5d6ab64b67e2636b30734..08835b5e1edb550b4e14f32bee9e4dcec78650aa 100644 --- a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/OsiMockServerExtension.java +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/extension/OsiMockServerExtension.java @@ -1,107 +1,69 @@ package de.ozgcloud.nachrichten.postfach.osiv2.extension; -import static org.assertj.core.api.Assertions.*; - -import java.util.List; -import java.util.function.Supplier; - -import jakarta.ws.rs.core.Response; - import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; -import org.keycloak.admin.client.resource.RealmResource; -import org.keycloak.representations.idm.ClientRepresentation; -import org.keycloak.representations.idm.ClientScopeRepresentation; import org.mockserver.client.MockServerClient; import org.testcontainers.containers.MockServerContainer; +import org.testcontainers.containers.output.OutputFrame; import org.testcontainers.utility.DockerImageName; -import dasniko.testcontainers.keycloak.KeycloakContainer; import lombok.Getter; import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +@Log4j2 @Getter @RequiredArgsConstructor public class OsiMockServerExtension implements BeforeAllCallback, AfterAllCallback, AfterEachCallback { - private MockServerClient mockServerClient; - private MockServerContainer mockServerContainer; - private KeycloakContainer keycloakContainer; + private MockServerClient mockClient; + private final MockServerContainer mockServerContainer = new MockServerContainer(DockerImageName.parse("mockserver/mockserver") + .withTag("mockserver-5.15.0")) + .withLogConsumer(outputFrame -> { + var line = outputFrame.getUtf8String().stripTrailing(); + if (outputFrame.getType() == OutputFrame.OutputType.STDERR) { + LOG.error(line); + } else { + LOG.info(line); + } + }); @Override public void beforeAll(ExtensionContext context) { - setupPostfachMockServer(); - setupKeycloak(); + setupMockServer(); } @Override public void afterEach(ExtensionContext context) { - mockServerClient.reset(); + mockClient.reset(); } @Override public void afterAll(ExtensionContext context) { mockServerContainer.stop(); - mockServerClient.stop(); - keycloakContainer.stop(); + mockClient.stop(); } - private void setupPostfachMockServer() { - mockServerContainer = new MockServerContainer(DockerImageName.parse("mockserver/mockserver") - .withTag("mockserver-5.15.0")); + private void setupMockServer() { mockServerContainer.start(); - mockServerClient = new MockServerClient(mockServerContainer.getHost(), mockServerContainer.getServerPort()); - } - - private void setupKeycloak() { - keycloakContainer = new KeycloakContainer("quay.io/keycloak/keycloak:24.0.5"); - keycloakContainer.start(); - try (var keycloak = keycloakContainer.getKeycloakAdminClient()) { - keycloak.tokenManager().getAccessToken(); - var masterRealm = keycloak.realm("master"); - var clientScopes = List.of("default", "access_urn:some:scope:for:ozgkopfstelle"); - clientScopes.forEach(scope -> createClientScope(masterRealm, scope)); - createPostfachClient(masterRealm, clientScopes); - } - } - - private void createPostfachClient(RealmResource realmResource, List<String> clientScopes) { - var clients = realmResource.clients(); - var postfach = new ClientRepresentation(); - postfach.setClientId("OZG-Kopfstelle"); - postfach.setSecret("changeme"); - postfach.setOptionalClientScopes(clientScopes); - postfach.setServiceAccountsEnabled(true); - postfach.setEnabled(true); - verifyResponseOk(() -> clients.create(postfach)); - } - - private void createClientScope(RealmResource realmResource, String scopeName) { - var clientScopes = realmResource.clientScopes(); - var clientScopeRepresentation = new ClientScopeRepresentation(); - clientScopeRepresentation.setName(scopeName); - clientScopeRepresentation.setProtocol("openid-connect"); - verifyResponseOk(() -> clientScopes.create(clientScopeRepresentation)); - } - - private void verifyResponseOk(Supplier<Response> responseSupplier) { - try (var response = responseSupplier.get()) { - assertThat(response.getStatus()).isEqualTo(201); - } + mockClient = new MockServerClient( + mockServerContainer.getHost(), + mockServerContainer.getServerPort() + ); } - public String getTokenUri() { - return getAuthProtocolUrl() + "/token"; + public String getAccessTokenUrl() { + return getMockServerUrl() + "/access-token"; } - public String getAuthProtocolUrl() { - return keycloakContainer.getAuthServerUrl() + "/realms/master/protocol/openid-connect"; + public String getPostfachFacadeUrl() { + return getMockServerUrl(); } - public String getPostfachMockServerUrl() { - return "http://" + mockServerClient.remoteAddress().getHostName() + ":" + mockServerClient.remoteAddress().getPort(); + private String getMockServerUrl() { + return "http://" + mockClient.remoteAddress().getHostName() + ":" + mockClient.remoteAddress().getPort(); } } diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/JsonUtil.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/JsonUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..5353606ac693ef6d98d03dc58d77d900c4943279 --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/JsonUtil.java @@ -0,0 +1,14 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.factory; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import lombok.SneakyThrows; + +public class JsonUtil { + private static final ObjectMapper jsonMapper = new ObjectMapper(); + + @SneakyThrows + public static String toJson(Object object) { + return jsonMapper.writeValueAsString(object); + } +} diff --git a/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/JwtFactory.java b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/JwtFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..08049761da834ac240d145c80034413bf7526650 --- /dev/null +++ b/src/test/java/de/ozgcloud/nachrichten/postfach/osiv2/factory/JwtFactory.java @@ -0,0 +1,51 @@ +package de.ozgcloud.nachrichten.postfach.osiv2.factory; + +import static groovy.json.JsonOutput.*; + +import java.util.List; +import java.util.Map; + + +import de.ozgcloud.nachrichten.postfach.osiv2.extension.Jwt; +import lombok.SneakyThrows; + +public class JwtFactory { + + public static final String CLIENT_ID = "ITCase OZG Kopfstelle"; + public static final String RESOURCE_URN = "urn:dataport:osi:postfach:rz2:itcase:sh"; + public static final List<String> CLIENT_SCOPES = List.of("default", "access_urn:itcase:scope:ozgkopfstelle"); + + public static Jwt createAccessTokenExampleWithExpireIn(Integer expireInSeconds) { + var nowEpochSeconds = System.currentTimeMillis() / 1000; + return Jwt.create( + Map.of( + "alg", "RS256", + "kid", "...", + "x5t", "...", + "typ", "at+jwt" + ), + Map.of( + "iss", "https://idp.serviceportal-stage.schleswig-holstein.de/webidp2", + "nbf", nowEpochSeconds, + "iat", nowEpochSeconds, + "exp", nowEpochSeconds + expireInSeconds, + "aud", RESOURCE_URN, + "scope", String.join(" ", CLIENT_SCOPES), + "client_id", CLIENT_ID, + "client_tenant", "urn:osp:names:realm:stage:sh", + "jti", "..." + ), + "signature".getBytes()); + } + + @SneakyThrows + public static String createTokenResponse(Jwt accessToken) { + return toJson(Map.of( + "access_token", accessToken.token(), + "token_type", "Bearer", + "expires_in", accessToken.expireTime() - accessToken.issuedAt(), + "scope", accessToken.scope() + )); + } + +}