diff --git a/src/test/java/de/ozgcloud/admin/security/AuthenticationExceptionTestFactory.java b/src/test/java/de/ozgcloud/admin/security/AuthenticationExceptionTestFactory.java index 3727bfc0c968540bafa8abe9dffdb6df9add2972..daf3d634ab66c34ea6f6e393d92681b69ed69b59 100644 --- a/src/test/java/de/ozgcloud/admin/security/AuthenticationExceptionTestFactory.java +++ b/src/test/java/de/ozgcloud/admin/security/AuthenticationExceptionTestFactory.java @@ -31,6 +31,7 @@ public class AuthenticationExceptionTestFactory { @Builder public static class DummyAuthenticationException extends AuthenticationException { + @SuppressWarnings("unused") private String msg; DummyAuthenticationException(String msg) { diff --git a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationWithKeycloakITCase.java b/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationWithKeycloakITCase.java deleted file mode 100644 index e336b21c6bde4f0904228557051a411c687276a9..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/security/SecurityConfigurationWithKeycloakITCase.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright - * (C) 2024 Das Land Schleswig-Holstein vertreten durch das - * Minis - * erium für Energiewende, Klimaschutz, Umwelt und Natur Zentrales - * IT-Management - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald diese von der - * Europäischen Kommission genehmigt wurden - Folgeversionen der EUPL - * - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften gefordert oder in - * schriftlicher Form vereinbart, wird die unter der Lizenz verbreitete Software - * "so wie sie ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. Die sprachspezifischen - * Genehmigungen und Beschränkungen unter der Lizenz sind dem Lizenztext zu - * entnehmen. - */ -package de.ozgcloud.admin.security; - -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import java.net.URI; -import java.util.Collections; -import java.util.Map; - -import org.apache.http.client.utils.URIBuilder; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.http.MediaType; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; -import org.springframework.test.web.servlet.MockMvc; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClient; - -import dasniko.testcontainers.keycloak.KeycloakContainer; -import de.ozgcloud.admin.RootController; -import de.ozgcloud.common.test.DataITCase; -import lombok.SneakyThrows; - -@DataITCase -@AutoConfigureMockMvc -class SecurityConfigurationWithKeycloakITCase { - @Autowired - private MockMvc mockMvc; - - static KeycloakContainer keycloak; - - @BeforeAll - static void setupKeycloakContainer() { - keycloak = new KeycloakContainer().withRealmImportFile("keycloak/realm-export.json"); - keycloak.start(); - } - - @AfterAll - static void closeKeycloakContainer() { - keycloak.close(); - } - - @DynamicPropertySource - static void registerResourceServerIssuerProperty(DynamicPropertyRegistry registry) { - registry.add("spring.security.oauth2.resourceserver.jwt.issuer-uri", () -> keycloak.getAuthServerUrl() + "/realms/by-kiel-dev"); - } - - @Nested - class TestSecuredEndpointWithKeycloakToken { - @SneakyThrows - @ParameterizedTest - @ValueSource(strings = { - "/api/environment", - "/configserver/name/profile", - "/api", "/api/configuration", "/api/configuration/param", - }) - void shouldGetAccessWithToken() { - String token = getToken(); - - var result = mockMvc.perform(get(RootController.PATH).header("Authorization", token)); - - result.andExpect(status().isOk()); - } - - @SneakyThrows - String getToken() { - MultiValueMap<String, String> formData = setPostBodyForToken(); - - Map<String, String> resultBody = performPostRequestToKeycloak(formData); - - return "Bearer " + resultBody.get("access_token").toString(); - } - - MultiValueMap<String, String> setPostBodyForToken() { - MultiValueMap<String, String> formData = new LinkedMultiValueMap<>(); - formData.put("grant_type", Collections.singletonList("password")); - formData.put("client_id", Collections.singletonList("admin")); - formData.put("username", Collections.singletonList("admin-test")); - formData.put("password", Collections.singletonList("Password")); - return formData; - } - - @SuppressWarnings("unchecked") - @SneakyThrows - Map<String, String> performPostRequestToKeycloak(MultiValueMap<String, String> formData) { - RestClient restClient = RestClient.create(); - URI authorizationURI = new URIBuilder(keycloak.getAuthServerUrl() + "/realms/by-kiel-dev/protocol/openid-connect/token").build(); - var response = restClient.post().uri(authorizationURI) - .contentType(MediaType.APPLICATION_FORM_URLENCODED) - .body(formData); - return response.retrieve().body(Map.class); - - } - - } -} \ No newline at end of file diff --git a/src/test/java/de/ozgcloud/admin/security/WithJwt.java b/src/test/java/de/ozgcloud/admin/security/WithJwt.java deleted file mode 100644 index ed3e4d36b99a451a98c1b882b0c8a047da4f4157..0000000000000000000000000000000000000000 --- a/src/test/java/de/ozgcloud/admin/security/WithJwt.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur - * Zentrales IT-Management - * - * Lizenziert unter der EUPL, Version 1.2 oder - sobald - * diese von der Europäischen Kommission genehmigt wurden - - * Folgeversionen der EUPL ("Lizenz"); - * Sie dürfen dieses Werk ausschließlich gemäß - * dieser Lizenz nutzen. - * Eine Kopie der Lizenz finden Sie hier: - * - * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * - * Sofern nicht durch anwendbare Rechtsvorschriften - * gefordert oder in schriftlicher Form vereinbart, wird - * die unter der Lizenz verbreitete Software "so wie sie - * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN - - * ausdrücklich oder stillschweigend - verbreitet. - * Die sprachspezifischen Genehmigungen und Beschränkungen - * unter der Lizenz sind dem Lizenztext zu entnehmen. - */ -package de.ozgcloud.admin.security; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.time.Instant; -import java.util.Map; -import java.util.Optional; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.jwt.Jwt; -import org.springframework.security.test.context.support.WithSecurityContext; -import org.springframework.security.test.context.support.WithSecurityContextFactory; -import org.springframework.util.StringUtils; - -import com.nimbusds.jwt.JWTClaimNames; - -import lombok.RequiredArgsConstructor; -import net.minidev.json.JSONObject; -import net.minidev.json.parser.JSONParser; -import net.minidev.json.parser.ParseException; - -/** - * Annotation to setup test {@link SecurityContext} with an {@link Authentication}. Adjusted from source: - * com.c4_soft.springaddons.security.oauth2.test.annotations.WithJwt Author: Jérôme Wacongne <ch4mp@c4-soft.com> - */ -@Target({ ElementType.METHOD, ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -@Documented -@WithSecurityContext(factory = WithJwt.AuthenticationFactory.class) -public @interface WithJwt { - - String value() default ""; - - String bearerString() default AuthenticationFactory.DEFAULT_BEARER; - - String headers() default AuthenticationFactory.DEFAULT_HEADERS; - - @RequiredArgsConstructor - final class AuthenticationFactory implements WithSecurityContextFactory<WithJwt> { - static final String DEFAULT_BEARER = "test.jwt.bearer"; - static final String DEFAULT_HEADERS = "{\"alg\": \"none\"}"; - - private final Converter<Jwt, ? extends AbstractAuthenticationToken> jwtAuthenticationConverter; - - @Override - public SecurityContext createSecurityContext(WithJwt annotation) { - var auth = authentication(annotation); - - var securityContext = SecurityContextHolder.createEmptyContext(); - securityContext.setAuthentication(auth); - - return securityContext; - } - - private AbstractAuthenticationToken authentication(WithJwt annotation) { - var claims = parseJson(annotation.value()); - var headers = parseJson(annotation.headers()); - var bearerString = annotation.bearerString(); - - var now = Instant.now(); - var iat = Optional.ofNullable((Integer) claims.get(JWTClaimNames.ISSUED_AT)).map(Instant::ofEpochSecond).orElse(now); - var exp = Optional.ofNullable((Integer) claims.get(JWTClaimNames.EXPIRATION_TIME)).map(Instant::ofEpochSecond) - .orElse(now.plusSeconds(42)); - - var jwt = new Jwt(bearerString, iat, exp, headers, claims); - - return jwtAuthenticationConverter.convert(jwt); - } - - private static Map<String, Object> parseJson(String json) { - if (!StringUtils.hasText(json)) { - return Map.of(); - } - try { - return new JSONParser(JSONParser.MODE_PERMISSIVE).parse(json, JSONObject.class); - } catch (final ParseException e) { - throw new RuntimeException("Invalid JSON payload in @WithJwt"); - } - } - } -}