/* * 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 static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import org.apache.http.HttpHeaders; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; 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.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import de.ozgcloud.admin.common.user.UserRole; import de.ozgcloud.common.test.DataITCase; import lombok.SneakyThrows; @DataITCase @AutoConfigureMockMvc class SecurityConfigurationITCase { @Autowired private MockMvc mockMvc; @DisplayName("without authentication") @Nested class TestWithoutAuthentication { @DisplayName("allow for not found") @SneakyThrows @ParameterizedTest @ValueSource(strings = { "/actuator", "/actuator/x", "/actuator/x/y", "/configserver", "/configserver/x", }) void shouldAllowForNotFound(String path) { var result = doPerform(path); result.andExpect(status().isNotFound()); } @DisplayName("allow") @SneakyThrows @ParameterizedTest @ValueSource(strings = { "/api/environment", "/configserver/name/profile" }) void shouldAllow(String path) { var result = doPerform(path); result.andExpect(status().isOk()); } @SneakyThrows @ParameterizedTest @ValueSource(strings = { "/api", "/api/configuration", "/api/configuration/settings", "/unknown", }) void shouldDeny(String path) { var result = doPerform(path); result.andExpect(status().isUnauthorized()); } @DisplayName("deny with problem details") @Nested class TestDenyWithProblemDetails { @SneakyThrows @Test void shouldHaveStatus() { var result = doPerform("/api"); result.andExpect(jsonPath("$.status").value(HttpStatus.UNAUTHORIZED.value())); } @SneakyThrows @Test void shouldHaveInstanceURI() { var result = doPerform("/api"); result.andExpect(jsonPath("$.instance").value("/api")); } @Test @SneakyThrows void shouldHaveErrorDetailInBody() { var result = doPerform("/api"); result.andExpect(jsonPath("$.detail").value("Full authentication is required to access this resource")); } @Test @SneakyThrows void shouldHaveHeader() { var result = doPerform("/api"); result.andExpect(header().string(HttpHeaders.WWW_AUTHENTICATE, "Bearer realm=\"Restricted Content\"")); } } @SneakyThrows private ResultActions doPerform(String path) { return mockMvc.perform(get(path).header("Authorization", "invalid")); } } @DisplayName("with authentication") @Nested class TestWithAuthentication { static final String CLAIMS = """ { "preferredUsername": "testUser", "scope": "openid testscope" }"""; @SneakyThrows @ParameterizedTest @ValueSource(strings = { "/api/environment", "/configserver/name/profile", "/api", "/api/configuration" }) @WithJwt(CLAIMS) void shouldAllow(String path) { var result = doPerformAuthenticated(path); result.andExpect(status().isOk()); } @Test @SneakyThrows @WithJwt(CLAIMS) void shouldForbid() { var result = doPerformAuthenticated("/api/configuration/settings"); result.andExpect(status().isForbidden()); } @SneakyThrows private ResultActions doPerformAuthenticated(String path) { return mockMvc.perform(get(path)); } } @DisplayName("with admin role") @Nested class TestWithAdminRole { @Test @SneakyThrows @WithMockUser(roles = UserRole.ADMIN_USER) void shouldAllow() { var result = mockMvc.perform(get("/api/configuration/settings")); result.andExpect(status().isOk()); } } }